题意:
一个物品重w效力t 给出所有n个物品 有q个询问 每个询问输出w的和为m同时t的和为s的方案
思路:
明显就是01背包 只不过一个东西在两个维度上有价值 由于w只有50因此可以状压
先想如何输出方案 利用f[i][j]表示sumw=i同时sumt=j时候装进包里的最后一个物品 那么输出这个物品后i和j都减去这个物品的w和t 就可以到达新的状态 这样可以一直到背包为空 最多循环50次
最难得是dp过程 之前提到状压 这题精髓就是状压后50位状态一起转移 用g[i]表示sumt=i时包含的sumw 对于一个重w效力t的物体 g[i]->g[i+t] 同时所有sumw->sumw+w 这样相当于少for了一维 然后再把g[i+t]中从0变成1的sumw的f数组对应记录一下
PS:
__builtin_ctz() 从ftiasch代码中看到了这个函数 蛮方便的 这是对于一个unsigned int返回二进制最后一个1后有几个0 那么__builtin_ctzll()则是针对unsigned long long的
做了3天数模高教社杯比赛 累成狗不说 刷题节奏完全断了…… 要快点找回感觉…… 补题补题补题补题……
代码:
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<vector>
using namespace std;
typedef long long LL;
#define Q 401
#define N 51
#define M 200001
#define inf 2147483647
#define lowbit(x) (x&(-x))
int t, n, q;
short f[N][M];
LL g[M];
int v[Q], w[Q], aw[Q], av[Q];
int main() {
int i, j, fv, fw;
LL k, tmp;
scanf("%d", &t);
while (t--) {
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
g[0] = 1;
scanf("%d%d", &n, &q);
for (i = 1; i <= n; i++)
scanf("%d%d", &w[i], &v[i]);
for (i = 1; i <= q; i++)
scanf("%d%d", &aw[i], &av[i]);
for (i = 1; i <= n; i++) {
for (j = M - 1; j >= v[i]; j--) {
k = ((g[j - v[i]] << w[i]) & ((1LL << N) - 1)) | g[j];
tmp = k ^ g[j];
g[j] = k;
while (tmp) {
f[__builtin_ctzll(tmp)][j] = i;
tmp ^= lowbit(tmp);
}
}
}
for (i = 1; i <= q; i++) {
fw = aw[i];
fv = av[i];
if (f[fw][fv]) {
j = f[fw][fv];
printf("%d", j);
fw -= w[j];
fv -= v[j];
for (; fw && fv;) {
j = f[fw][fv];
printf(" %d", j);
fw -= w[j];
fv -= v[j];
}
putchar('\n');
} else
puts("No solution!");
}
}
return 0;
}