题意:
一个物品重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; }