ZOJ 3812 We Need Medicine

题意:

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


你可能感兴趣的:(dp,ZOJ)