题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3812
题目大意:给出n个物品,每个物品有两种属性Wi,Ti,有q组查询,每组查询要求在n个物品中选出一些,并使得两个属性的和为Mi,Si。
思路:二维费用的背包,但是太大了会TLE或者mle……
我们来观察一下状态 dp[i][j][k]表示使用前i个物品,是否可以凑出第一个属性j,第二个属性k,
注意第一个属性最多只有50,那么可以用一个二进制数来表示是否能凑出第一个属性的情况(unsigned long long)第i位为1表示可以凑出i。
然后再加上就地滚动,就ok了
代码:
#include <cstdlib> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <map> using namespace std; typedef unsigned long long ull; const int N = 200010; const int maxn = 420; const int M = 51; short ans[N][M]; ull f[N]; int W[maxn], T[maxn]; int t, n, q; map<ull, int> mp; int main() { for(int i = 1;i <= M + 1;++i) mp[1LL << (i - 1LL)] = i; scanf("%d", &t); while(t--) { scanf("%d%d",&n, &q); for(int i = 1; i <= n; i++) { scanf("%d%d", &W[i], &T[i]); } memset(ans, 0, sizeof(ans)); memset(f, 0, sizeof(f)); f[0] = 1; for(int i = 1; i <= n; i++) { for(int j = 200000; j >= T[i]; j--) { ull tmp = f[j]; f[j] |= (f[j - T[i]] << W[i]); ///转移:加上w[i]这个物品 for(ull k = tmp ^ f[j]; k != 0; k &= k - 1) /// 记录答案 { ull x = (k ^ (k - 1)) & k; ans[j][mp[x] - 1] = i; } } } int m, s, p; for(int i = 0; i < q; i++) { scanf("%d%d", &m, &s); if(!ans[s][m]) printf("No solution!\n"); else { printf("%d", ans[s][m]); p = ans[s][m]; m -= W[p], s -= T[p]; while(m) { p = ans[s][m]; printf(" %d",p); m -= W[p], s -= T[p]; } printf("\n"); } } } return 0; }