This commit is contained in:
HuangHai
2025-08-30 18:35:01 +08:00
commit 1f397eca87
6258 changed files with 1487241 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
#include <bits/stdc++.h>
using namespace std;
int cnt;
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++)
if (s[i] == '1') cnt++;
printf("%d\n", cnt);
return 0;
}

View File

@@ -0,0 +1,10 @@
## $2019CSP$普及组复赛真题解析
[$P5660$ $[CSP-J2019]$ 数字游戏](https://www.luogu.com.cn/problem/P5660)
[$P5661$ $[CSP-J2019]$ 公交换乘](https://www.luogu.com.cn/problem/P5661)
[$P5662$ $[CSP-J2019]$ 纪念品](https://www.luogu.com.cn/problem/P5662)
[$P5663$ $[CSP-J2019]$ 加工零件](https://www.luogu.com.cn/problem/P5663)

View File

@@ -0,0 +1,36 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Node {
int f;
int p;
int t;
} a[N];
int ans;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i].f >> a[i].p >> a[i].t;
for (int i = 1; i <= n; i++) {
if (a[i].f == 0)
ans += a[i].p;
else {
bool flag = false;
for (int j = max(1,j-45); j < i; j++) {
if (a[j].f == 0 && a[i].t - a[j].t <= 45 && a[j].p >= a[i].p) {
flag = true;
a[j].f = -1;
break;
}
}
if (!flag) ans += a[i].p;
}
}
printf("%d\n", ans);
return 0;
}

View File

@@ -0,0 +1,38 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Node {
int f;
int p;
int t;
} a[N];
int ans;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i].f >> a[i].p >> a[i].t;
for (int i = 1; i <= n; i++) {
if (a[i].f == 0)
ans += a[i].p;
else {
bool flag = false;
//加入了优化点,因为一个站点最少1分钟,所以如果在45分钟以内有效的话,则最多是向前45个站点提供的优惠卷
//如果,成功将O(N^2)的时间复杂度,优化为O(N*45)的时间复杂度,因为N高达1e5,所以可以大大降低运行时长
for (int j = max(i - 45, 1); j < i; j++) {
if (a[j].f == 0 && a[i].t - a[j].t <= 45 && a[j].p >= a[i].p) {
flag = true;
a[j].f = -1;
break;
}
}
if (!flag) ans += a[i].p;
}
}
printf("%d\n", ans);
return 0;
}

View File

@@ -0,0 +1,73 @@
##[$P5662$ [$CSP$-$J2019$] 纪念品](https://www.luogu.com.cn/problem/P5662)
### 前序知识
[$AcWing$ $2$. $01$背包问题](https://www.cnblogs.com/littlehb/p/15366309.html)
[$AcWing$ $3$. 完全背包问题](https://www.cnblogs.com/littlehb/p/15370457.html)
### 前序练习题
$01$背包应用 D:\python\TangDou\LanQiao12ShengSai 第四题 农民伯伯种蔬菜
完全背包应用 D:\python\TangDou\LanQiao12GuoSai 小蓝买瓜子
多维限制的完全背包应用 D:/python/LQBS2019/第十届蓝桥杯大赛青少年创意编程C++组省赛(8).pdf
赛瓦维斯特定理+完全背包应用 汤圆组合 D:\python\TangDou\LanQiaoBei13ShengSai_ZJ_II\编程题\3.1.png
### 算法
稍微一读题,每种纪念品可以买无数次,有总钱数,价格,以及天数,应该都可以想到是完全背包。
**总钱数**:背包容量;
**纪念品价格**:每件物品的 **价值** 和 **体积**;
**纪念品数量**:物品的种类。
可是到这就又犯难了:纪念品的价格每天都在变,还有手里的钱和纪念品数量在变,还要买或卖,如果都传入状态中,肯定炸空间。
我们一个一个解决:
纪念品的价格尽管每天在变,但题目给出了每天每种的价格。
手里钱、纪念品数以及买卖其实用一个简单的思路就足以解决:我们可以每天早上把手里所有纪念品全部卖掉,获得一天的本金,$fufu$,如果卖掉了有一些不该卖的,就再买回来,这样相当于每天每件纪念品就只有买或不买,就可以转化为 **典型的完全背包**,这其实也是一种变相的贪心策略,**我们只要保证今天一天的收益最高就行**,而不用管后面几天是否能赚最多钱,因为最优情况也被保存在$dp$数组里,在后面的$dp$中会被使用并传递。
所以结论是:该背包最重要的状态就是手里的钱,因为它能保存住整体的最优情况在该日的策略,故这题的$dp$数组可以优化到一维。
不多说,上代码。
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 10010;
int T, n, m;
int f[M];
int w[N][N]; //第i天,第j支股票的价格
int main() {
scanf("%d%d%d", &T, &n, &m);
for (int i = 1; i <= T; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &w[i][j]);
//朴素版本
for (int t = 1; t < T; t++) { //枚举每一天
memset(f, 0, sizeof f); // dp数组
for (int i = 1; i <= n; i++) //枚举前i种物品
for (int j = w[t][i]; j <= m; j++) //枚举剩余体积
f[j] = max(f[j], f[j - w[t][i]] + w[t + 1][i] - w[t][i]);
//收益累加
m += f[m];
}
printf("%d\n", m);
return 0;
}
```

View File

@@ -0,0 +1,28 @@
#include <bits/stdc++.h>
// 视频讲解:https://www.bilibili.com/video/BV1SL4y187x6
// 初步思考:t=1,t=2开始尝试解决
using namespace std;
const int N = 110;
int w[N][N], f[N][10010];
int main() {
int t, n, m;
cin >> t >> n >> m;
for (int i = 1; i <= t; i++)
for (int j = 1; j <= n; j++)
cin >> w[i][j];
if (t == 1)
printf("%d\n", m);
else if (t == 2) {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
f[i][j] = f[i - 1][j];
if (j >= w[1][i])
f[i][j] = max(f[i][j], f[i][j - w[1][i]] + w[2][i] - w[1][i]);
}
printf("%d\n", m + f[n][m]);
}
return 0;
}

View File

@@ -0,0 +1,26 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int w[N][N], f[N][10010];
int main() {
int t, n, m;
cin >> t >> n >> m;
for (int i = 1; i <= t; i++)
for (int j = 1; j <= n; j++)
cin >> w[i][j];
for (int k = 1; k < t; k++) {
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
f[i][j] = f[i - 1][j];
if (j >= w[k][i])
f[i][j] = max(f[i][j], f[i][j - w[k][i]] + w[k + 1][i] - w[k][i]);
}
m += f[n][m];
}
printf("%d\n", m);
return 0;
}

View File

@@ -0,0 +1,29 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 10010;
int T, n, m;
int f[M];
int w[N][N]; //第i天,第j支股票的价格
int main() {
//未来天数 T,纪念品数量 N,小伟现在拥有的金币数量 M
cin >> T >> n >> m;
for (int i = 1; i <= T; i++) //未来T天
for (int j = 1; j <= n; j++) //每个纪念品
cin >> w[i][j]; //价格
for (int t = 1; t < T; t++) { //枚举每一天
memset(f, 0, sizeof f); // dp数组
for (int i = 1; i <= n; i++) //枚举前i种物品
for (int j = w[t][i]; j <= m; j++) //枚举剩余体积
f[j] = max(f[j], f[j - w[t][i]] + w[t + 1][i] - w[t][i]); //收益=w[t + 1][i] - w[t][i]
//因为赚了钱,所以后面一天的初始金额变大,这是一个逐步累加的过程.也就是背包容量变大
m += f[m];
}
printf("%d\n", m);
return 0;
}

View File

@@ -0,0 +1,31 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 10010;
int T, n, m;
int f[M];
int w[N][N]; //第i天,第j支股票的价格
int main() {
//未来天数 T,纪念品数量 N,小伟现在拥有的金币数量 M
cin >> T >> n >> m;
for (int i = 1; i <= T; i++) //未来T天
for (int j = 1; j <= n; j++) //每个纪念品
cin >> w[i][j]; //价格
//优化版本
for (int t = 1; t < T; t++) { //枚举每一天
memset(f, 0, sizeof f); // dp数组
for (int i = 1; i <= n; i++) //枚举前i种物品
if (w[t + 1][i] > w[t][i]) //可以加的优化:只有在价值为正数时才会考虑使用该物品
for (int j = w[t][i]; j <= m; j++) //枚举剩余体积
f[j] = max(f[j], f[j - w[t][i]] + w[t + 1][i] - w[t][i]);
//收益累加
m += f[m];
}
printf("%d\n", m);
return 0;
}

193
TangDou/CSP-J/J2_2019/4.md Normal file
View File

@@ -0,0 +1,193 @@
## 模型
给定一张包含 $n$ 个点和 $m$ 条边的 **无向图**,再给定 $q$ 个询问:$a_i,L_i$,判断是否存在一条从$1$号点走到$a_i$号点的恰好经过$L$条边的路径。
<center><img src='https://cdn.luogu.com.cn/upload/image_hosting/tfom5z2s.png'></center>
设求第$3$点为第$3$阶段时,点$1$是否需要提供原材料。
$【3,3】 => 【2,2】,【4,2】$
$【2,2】 => 【1,1】,【3,1】$
$【4,2】 => 【3,1】,【5,1】$
$【1,1】 => 【2,0】,【5,0】$
$【3,1】 => 【2,0】,【4,0】$
$【5,1】 => 【1,0】,【4,0】$ # 此处$1$需要提供原材料
比较容易想到对于每个询问进行暴搜,若点$1$为$0$,则$Yes$
时间复杂度很高,必然超时。
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 1e5 + 10, M = N << 1;
// 20个测试点,过了7个,得了35分,其它TLE
// 邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool bfs(int start, int dist) {
queue<PII> q;
q.push({start, dist});
while (q.size()) {
auto u = q.front();
q.pop();
if (u.x == 1 && u.y == 0) return true; // 在用完所有步数后,到达1号点,成功!
for (int i = h[u.x]; ~i; i = ne[i]) {
int v = e[i];
if (u.y) q.push({v, u.y - 1});
}
}
return false;
}
int main() {
memset(h, -1, sizeof h);
int n, m, Q;
cin >> n >> m >> Q;
while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a); // 无向图,双向建边
}
while (Q--) {
int u, dist;
cin >> u >> dist;
cout << (bfs(u, dist) ? "Yes" : "No") << endl;
}
return 0;
}
```
## 算法
(最短路,$BFS$) $O(n+m+q)$
本题关键点:
> 如果存在一条长度是 $L$ 的路径,其中 $L>0$,那么我们可以在其中任意一条边上来回走,就可以构造出来长度是 $L+2,L+4,L+6,…$的路径。
因此当我们想判断是否存在长度是 $L$ 的路径时,只需判断是否 **存在长度小于等于 $L$**,且 **奇偶性** 和 $L$ 相同的路径即可。
因此我们可以预处理出从$1$号点出发,到每个点长度为奇数的最短路径和长度为偶数的最短路径。
这里可以使用拆点技巧来构造新图,类似于$DP$中的[状态机模型](https://www.acwing.com/problem/content/1051/):
* 将原图中的每个点 $v$ 拆成两个新点:偶点$v_0$和奇点$v_1$;
* 将原图中的每条边 $(u,v)$ 拆成两条新边:$(u_0,v_1)$ 和 $(u_1,v_0)$;
那么在新图中从 $1$ 号点走到 $v_0$ 的所有路径,对应在原图中从 $1$ 号点走到 $v$ 的所有 **长度是偶数** 的路径;在新图中从 $1$ 号点走到 $v_1$ 的所有路径,对应在原图中从 $1$ 号点走到 $v$ 的所有 **长度是奇数** 的路径。
因此在新图上求出$1$号点到其他所有点的最短路径,即可求出在原图中从$1$号点到其他所有点的长度是奇数和偶数的最短路径。
由于所有边的长度为$1$,因此可以用$BFS$求最短路。
以上我们处理了 $L_i>0$ 的情况,还需特判一下 $L_i==0$ 的情况,$L_i==0$ 表示一条边都不存在,即$1$号点与其他点均不连通,此时由于输入时 $L_i≥1$,因此直接输出 $No$即可。
#### 时间复杂度
使用$BFS$求最短路的时间复杂度是 $(n+m)$;
判断每个查询操作的时间复杂度是 $O(1)$;
因此总时间复杂度是 $O(n+m+q)$。
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define x first // 节点号
#define y second // 奇偶性
const int N = 100010, M = 200010;
int n, m, Q;
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dist[N][2]; // 奇偶最短路
queue<PII> q;
// 因边边权都是1,不需要Dijkstra或者Floyd,只需要bfs即可
// 由于本题是强调 奇偶性 ,所以,需要使用 拆点 的思想
// 所谓拆点,就是像状态机或者DP中扩展维度的思想,将奇偶点分开,把混沌的事情清晰化。
// dist[i][0]:表示是偶数步的点,值是最小步数
// dist[i][1]:表示是奇数步的点
void bfs() {
memset(dist, 0x3f, sizeof dist); // 预求最短,先设最大
dist[1][0] = 0; // 1是起点,走0步到达即偶点,步数:0
q.push({1, 0}); // 起点入队列
while (q.size()) {
auto u = q.front();
q.pop();
for (int i = h[u.x]; ~i; i = ne[i]) { // u点
PII v = {e[i], u.y ^ 1};
// u.y ^ 1 奇<->偶 互换
// 由于上面的 y=u.y ^ 1,使得(u.x,u.y)只向(x,u.y的奇偶变换后数) 转移
// 这样做,就起到的拆点作用,而并不是真正的把点拆开
if (dist[v.x][v.y] > dist[u.x][u.y] + 1) { // 如果利用u可以更新v这个点
dist[v.x][v.y] = dist[u.x][u.y] + 1; // 更新
q.push(v); // v的最短距离更新了,入队列,去更新更多的点
}
}
}
}
int main() {
cin >> n >> m >> Q;
memset(h, -1, sizeof h);
while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
bfs();
while (Q--) {
int a, l;
cin >> a >> l;
/*
如果不写下面的特判,会被欺负死:
3 1 1
2 3
1 2
第一行三个正整数 n,m 和 q,分别表示工人的数目、传送带的数目和工单的数目。
也就是3个工人,1个传送带,1个工单
传送带是2~3,也就是1是与世隔绝的
接下来 q 行,每行两个正整数 a 和 L,表示编号为 a 的工人想生产一个第 L 阶段的零件。
1号工人,想生产2阶段的零件
由于1号工人是与世隔绝的,没有人给它提供1阶段的零件,肯定返回No,此时需要特判,返回答案
*/
if (h[1] == -1)
puts("No");
else if (l >= dist[a][l & 1]) // l是奇数,并且大于奇数最短路,则可以通过+2,+4,+6...等方法来回磨,凑够l就完事
// l是偶数,并且大于偶数最短路,则可以通过+2,+4,+6...等方法来回磨,凑够l就完事
puts("Yes");
else
puts("No");
}
return 0;
}
```

View File

@@ -0,0 +1,53 @@
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 1e5 + 10, M = N << 1;
// 20个测试点,过了7个,得了35分,其它TLE
// 邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool bfs(int start, int dist) {
queue<PII> q;
q.push({start, dist});
while (q.size()) {
auto u = q.front();
q.pop();
if (u.x == 1 && u.y == 0) return true; // 在用完所有步数后,到达1号点,成功!
for (int i = h[u.x]; ~i; i = ne[i]) {
int v = e[i];
if (u.y) q.push({v, u.y - 1});
}
}
return false;
}
int main() {
// 链式前向星
memset(h, -1, sizeof h);
int n, m, Q;
cin >> n >> m >> Q;
while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a); // 无向图,双向建边
}
while (Q--) {
int u, dist;
cin >> u >> dist;
cout << (bfs(u, dist) ? "Yes" : "No") << endl;
}
return 0;
}

View File

@@ -0,0 +1,83 @@
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define x first // 节点号
#define y second // 奇偶性
const int N = 100010, M = 200010;
int n, m, Q;
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dist[N][2]; // 奇偶最短路 dist[5][0]:4,dist[6][1]:3
queue<PII> q;
// ① 因边边权都是1,不需要Dijkstra或者Floyd,只需要bfs即可
// ② 由于本题是强调 奇偶性 ,所以,需要使用 拆点 的思想
// ③ 所谓拆点,就是像状态机或者DP中扩展维度的思想,将奇偶点分开,把混沌的事情清晰化。
// dist[i][0]:表示是偶数步的点,值是最小步数
// dist[i][1]:表示是奇数步的点
void bfs() {
memset(dist, 0x3f, sizeof dist); // 预求最短,先设最大
dist[1][0] = 0; // 1是起点,0:理解为从1号出发,到1号结束,走了0步的结果,所以是偶数步,步数为0
q.push({1, 0}); // 起点入队列
while (q.size()) {
auto u = q.front();
q.pop();
for (int i = h[u.x]; ~i; i = ne[i]) { // u点
PII v = {e[i], u.y ^ 1}; // 0 ^ 1 = 1 1 ^ 1 =0
if (dist[v.x][v.y] > dist[u.x][u.y] + 1) { // 如果利用u可以更新v这个点
dist[v.x][v.y] = dist[u.x][u.y] + 1; // 更新
q.push(v); // v的最短距离更新了,入队列,去更新更多的点
}
}
}
}
int main() {
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
memset(h, -1, sizeof h);
cin >> n >> m >> Q;
while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
bfs();
while (Q--) {
int a, l;
cin >> a >> l;
/*
如果不写下面的特判,会被欺负死:
3 1 1
2 3
1 2
第一行三个正整数 n,m 和 q,分别表示工人的数目、传送带的数目和工单的数目。
也就是3个工人,1个传送带,1个工单
传送带是2~3,也就是1是与世隔绝的
接下来 q 行,每行两个正整数 a 和 L,表示编号为 a 的工人想生产一个第 L 阶段的零件。
1号工人,想生产2阶段的零件
由于1号工人是与世隔绝的,没有人给它提供1阶段的零件,肯定返回No,此时需要特判,返回答案
*/
if (h[1] == -1)
puts("No");
else if (l >= dist[a][l & 1]) // l是奇数,并且大于奇数最短路,则可以通过+2,+4,+6...等方法来回磨,凑够l就完事
// l是偶数,并且大于偶数最短路,则可以通过+2,+4,+6...等方法来回磨,凑够l就完事
puts("Yes");
else
puts("No");
}
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <bits/stdc++.h>
// https://blog.csdn.net/gnn8291/article/details/90632100
using namespace std;
int W; // 可以提起 W单位重量的东西,
int V; // 有一个能装V个单位体积的购物袋
int n; // 为商品种类数
// 注意多维限制的01背包,一般N值都比较小,100左右,太大就会编译出错了。
const int N = 110;
// 某种商品的重量、体积和让利金额
int a[N], b[N], c[N];
// DP数组
int f[N][N][N];
/**
10 9 4
8 3 6
5 4 5
3 7 7
4 5 4
答案:
9
2 4
*/
int main() {
cin >> W >> V >> n; // 输入
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i]; // 每一种商品的重量、体积和让利金额
for (int i = 1; i <= n; i++) { // 遍历每个种类
for (int j = 1; j <= W; j++) { // 遍历重量
for (int k = 1; k <= V; k++) { // 遍历体积
int x = f[i - 1][j - a[i]][k - b[i]] + c[i]; // 如果选了,结果是x
int y = f[i - 1][j][k]; // 如果不选,结果是y
if (j >= a[i] && k >= b[i]) // 如果剩余的重量和体积都够用的时候,尝试拿当前物品
f[i][j][k] = max(x, y); // 也是两个分支,一是不选,另一个是选,要取最大值
else
f[i][j][k] = y; // 装不下,不管是哪种原因装不下,都是这个分支
}
}
}
// 输出
cout << f[n][W][V] << endl; // 小惠能够得到的最大让利金额 
return 0;
}

View File

@@ -0,0 +1,61 @@
#include <bits/stdc++.h>
using namespace std;
int W; // 可以提起 w 单位重量的东西,
int V; // 有一个能装v个单位体积的购物袋
int n; // 为商品种类数
// 注意多维限制的01背包,一般N值都比较小,100左右,太大就会编译出错了。
const int N = 110;
// 某种商品的重量、体积和让利金额
int a[N], b[N], c[N];
// 在取得最大让利金额的时候,到底是拿了哪些商品?
string s[N][N][N];
// DP数组
int f[N][N][N];
/**
10 9 4
8 3 6
5 4 5
3 7 7
4 5 4
答案:
9
2 4
*/
int main() {
cin >> W >> V >> n; // 输入
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i]; // 每一种商品的重量、体积和让利金额
for (int i = 1; i <= n; i++) { // 遍历每个种类
for (int j = 1; j <= W; j++) { // 遍历重量(填表)
for (int k = 1; k <= V; k++) { // 遍历体积(填表)
int x = f[i - 1][j - a[i]][k - b[i]] + c[i]; // 选择当前物品
int y = f[i - 1][j][k]; // 不选当前物品
// 如果剩余的重量和体积都够用的时候,尝试拿当前物品
if (j >= a[i] && k >= b[i]) {
// 也是两个分支,一是不选,另一个是选,要取最大值
if (x > y) { // 要上合适
f[i][j][k] = x;
s[i][j][k] = s[i - 1][j - a[i]][k - b[i]] + " " + (char)(i + '0'); // 同时记录拿了这个物品
} else // 不要合适
f[i][j][k] = y;
} else {
f[i][j][k] = y; // 装不下,不管是哪种原因装不下,都是这个分支
s[i][j][k] = s[i - 1][j][k]; // 同时记录不拿当前物品,保持和之前一样的物品列表
}
}
}
}
// 输出
cout << f[n][W][V] << endl; // 小惠能够得到的最大让利金额
string str = s[n][W][V]; // 依次为从小到大排列的商品序号
cout << str.substr(1, str.size() - 1);
return 0;
}

View File

@@ -0,0 +1,28 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
string s[N];
// 如果扩展问题:取得最大值时,选择的是哪些物品?
// D:\python\TangDou\LanQiao12ShengSai\BC4.jpg
int main() {
// 物品个数n
cin >> n >> m; // m:背包容积,就是大V
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++)
for (int j = m; j >= w[i]; j--) {
if (f[j - w[i]] + v[i] > f[j]) {
f[j] = f[j - w[i]] + v[i];
s[j] = s[j - w[i]] + " " + (char)(i + '0');
}
}
cout << f[m] << endl;
cout << s[m] << endl;
return 0;
}