zoj 3812 We Need Medicine (dp 状压)

先贴一下转载的思路和代码,,,:http://blog.csdn.net/qian99/article/details/39138329

状压dp博大精深啊,以后看到n<=50都可以往状压上想,orz

We Need Medicine


Time Limit: 10 Seconds                                     Memory Limit: 65536 KB                                                     Special Judge


A terrible disease broke out! The disease was caused by a new type of virus, which will lead to lethal lymphoedema symptom. For convenience, it was named LL virus.

After several weeks of research, the scientists found the LL virus highly lethal and infectious. But more importantly, it has a long incubation period. Many victims were unaware of being infected until everything was too late. To prevent from the apocalypse, we need medicine!

Fortunately, after another several weeks of research, the scientists have finished the analysis of the LL virus. You need write a program to help them to produce the medicine.

The scientists provide you N kinds of chemical substances. For each substance, you can either use it exact Wi milligrams in a medicine, or not use it. Each selected substance will add Ti points of therapeutic effect value (TEV) to the medicine.

The LL virus has Q different variants. For each variant, you need design a medicine whose total weight equals to Mi milligrams and total TEV equals to Si points. Since the LL virus is spreading rapidly, you should start to solve this problem as soon as possible!

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains two integers N (1 <= N <= 400) and Q (1 <= Q <= 400).

For the next N lines, each line contains two integers Wi (1 <= Wi <= 50) and Ti (1 <= Ti <= 200000).

Then followed by Q lines, each line contains two integers Mi (1 <= Mi <= 50) and Si (1 <= Si <= 200000).

Output

For each test case, output Q lines. For the i-th line, output the indexes (1-based) of chemical substances in the i-th medicine, separated by a space. If there are multiple solutions, output any one. If there is no solution, output "No solution!" instead.

Sample Input

1
3 3
2 10
1 12
1 5
3 15
4 27
3 17

Sample Output

1 3
3 2 1
No solution!


Author: JIANG, Kai                                         Source: The 2014 ACM-ICPC Asia Mudanjiang Regional First Round

转自:http://blog.csdn.net/qian99/article/details/39138329

题意:给出n个物品,每个物品有两种属性Wi,Ti,有q组查询,每组查询要求在n个物品中选出一些,并使得两个属性的和为Mi,Si。

思路:刚开始看感觉是神题,后来仔细想了想,其实本质上就是个背包。最裸着写的话,那么就是dp[i][j][k]表示使用前i个物品,是否可以凑出第一个属性j,第二个属性k,要输出方案的话记录一下路径就可以了。一开始这么写了一发,加了一些乱七八糟的优化,还是会T。虽然这题时限还算宽,但这么写复杂度还是太高了。考虑到第一个属性最多只有50,那么可以用一个二进制数来表示是否能凑出第一个属性的情况,即:第i位为1表示可以凑出i。使用这种方法的好处是对于物品i可以直接算出第一种属性的组合情况,枚举一下新增的位,更新一下结果就行了。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<map>
  9
 10 #define N 410
 11 #define M 200010
 12 #define mod 6
 13 #define mod2 100000000
 14 #define ll long long
 15 #define ull unsigned long long
 16 #define maxi(a,b) (a)>(b)? (a) : (b)
 17 #define mini(a,b) (a)<(b)? (a) : (b)
 18
 19 using namespace std;
 20
 21 int T;
 22 int n,q;
 23 int w[N],t[N];
 24 int m,s;
 25 ull f[M];
 26 int ans[M][52];
 27 map<ull,int>mt;
 28
 29 void ini1()
 30 {
 31     int i;
 32     for(i=1;i<=52;i++){
 33         mt[ (1ll<<(i-1ll)) ]=i;
 34     }
 35 }
 36
 37 void ini()
 38 {
 39     int i,j;
 40     ull k,x;
 41     ull v;
 42     scanf("%d%d",&n,&q);
 43     memset(ans,0,sizeof(ans));
 44     memset(f,0,sizeof(f));
 45     for(i=1;i<=n;i++){
 46         scanf("%d%d",&w[i],&t[i]);
 47     }
 48     f[0]=1;
 49     for(i=1;i<=n;i++){
 50         for(j=200000;j>=t[i];j--){
 51             v=f[j];
 52             f[j] |= (f[j-t[i]]<<w[i]) & ( (1ll<<52)-1 );
 53             for(k=v ^ f[j];k>0;k &= (k-1) )
 54             {
 55                 x=(k ^(k-1))&k;
 56                 ans[j][ mt[x]-1 ]=i;
 57             }
 58         }
 59     }
 60 }
 61
 62 void solve()
 63 {
 64     int te;
 65     while(q--)
 66     {
 67         scanf("%d%d",&m,&s);
 68         if(ans[s][m]==0){
 69             printf("No solution!\n");
 70             continue;
 71         }
 72         else{
 73             printf("%d",ans[s][m]);
 74             te=ans[s][m];
 75             m-=w[te];
 76             s-=t[te];
 77             while(m!=0)
 78             {
 79                 printf(" %d",ans[s][m]);
 80                 te=ans[s][m];
 81                 m-=w[te];
 82                 s-=t[te];
 83             }
 84             printf("\n");
 85         }
 86     }
 87 }
 88
 89 void out()
 90 {
 91     //printf("%lld\n",ans);
 92     //cout<<ans<<endl;
 93 }
 94
 95
 96 int main()
 97 {
 98     ini1();
 99    // freopen("data.in","r",stdin);
100     scanf("%d",&T);
101     for(int cnt=1;cnt<=T;cnt++)
102    // while(T--)
103     //while(scanf("%I64d%I64d%I64d",&a,&b,&c)!=EOF)
104     {
105         ini();
106         solve();
107         //out();
108     }
109
110     return 0;
111 }

再贴一份自己加过注释的,状压dp博大精深啊:

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<map>
  9
 10 #define N 410
 11 #define M 200010
 12 #define mod 6
 13 #define mod2 100000000
 14 #define ll long long
 15 #define ull unsigned long long
 16 #define maxi(a,b) (a)>(b)? (a) : (b)
 17 #define mini(a,b) (a)<(b)? (a) : (b)
 18
 19 using namespace std;
 20
 21 int T;
 22 int n,q;
 23 int w[N],t[N];
 24 int m,s;
 25 ull f[M];
 26 int ans[M][52];
 27 map<ull,int>mt;
 28
 29 void ini1()
 30 {
 31     int i;
 32     for(i=1;i<=52;i++){
 33         mt[ (1ll<<(i-1ll)) ]=i;
 34         //printf(" i=%d mt=%I64d\n",i,(1ll<<(i-1ll)));
 35     }
 36 }
 37
 38 void ini()
 39 {
 40     int i,j;
 41     ull k,x;
 42     ull v,te;
 43     scanf("%d%d",&n,&q);
 44     memset(ans,0,sizeof(ans));
 45     memset(f,0,sizeof(f));
 46     for(i=1;i<=n;i++){
 47         scanf("%d%d",&w[i],&t[i]);
 48     }
 49     f[0]=1;
 50     for(i=1;i<=n;i++){
 51         //printf(" i=%d\n",i);
 52         //for(j=200000;j>=t[i];j--){
 53         for(j=200000;j>=t[i];j--){
 54             v=f[j];             //j原来存的值
 55             te=(f[j-t[i]]<<w[i]);
 56             f[j] |= (f[j-t[i]]<<w[i]) ;     //j原来存的值+转移后存的值
 57           //  printf("  j=%d v=%I64d f=%I64d\n",j,v,f[j]);
 58             for(k=v ^ f[j];k>0;k &= (k-1) )  //k初始化为新增出来的值,然后不断将k最右边的1减掉
 59            //  for(k=te;k>0;k &= (k-1) )
 60             {
 61                 x=(k ^(k-1))&k;         //取k最右边的1
 62                 ans[j][ mt[x]-1 ]=i;
 63                // printf("    k=%I64d x=%I64d mtx-1=%d ans=%d\n",k,x,mt[x]-1,ans[j][ mt[x]-1 ]);
 64             }
 65         }
 66     }
 67 }
 68
 69 void solve()
 70 {
 71     int te;
 72     while(q--)
 73     {
 74         scanf("%d%d",&m,&s);
 75         if(ans[s][m]==0){
 76             printf("No solution!\n");
 77             continue;
 78         }
 79         else{
 80             printf("%d",ans[s][m]);
 81             te=ans[s][m];
 82             m-=w[te];
 83             s-=t[te];
 84             while(m!=0)
 85             {
 86                 printf(" %d",ans[s][m]);
 87                 te=ans[s][m];
 88                 m-=w[te];
 89                 s-=t[te];
 90             }
 91             printf("\n");
 92         }
 93     }
 94 }
 95
 96 void out()
 97 {
 98     //printf("%lld\n",ans);
 99     //cout<<ans<<endl;
100 }
101
102
103 int main()
104 {
105     ini1();
106    // freopen("data.in","r",stdin);
107     //freopen("data.out","w",stdout);
108     scanf("%d",&T);
109     for(int cnt=1;cnt<=T;cnt++)
110    // while(T--)
111     //while(scanf("%I64d%I64d%I64d",&a,&b,&c)!=EOF)
112     {
113         ini();
114         solve();
115         //out();
116     }
117
118     return 0;
119 }
时间: 2024-10-10 20:09:06

zoj 3812 We Need Medicine (dp 状压)的相关文章

ZOJ 3812 We Need Medicine(牡丹江网络赛D题)

ZOJ 3812 We Need Medicine 题目链接 思路:dp[i][j][k]表示第i个物品,组成两个值为j和k的状态,这样会爆掉,所以状态需要转化一下 首先利用滚动数组,可以省去i这维,然后由于j最大记录到50,所以可以把状态表示成一个二进制数s,转化成dp[k] = s,表示组成k状态能组成s,这样空间复杂度就可以接受了,然后这题时限还可以,就这样去转移,然后记录下路径即可 代码: #include <cstdio> #include <cstring> #incl

zoj 3812 We Need Medicine(01背包)

题目链接:zoj 3812 We Need Medicine 题目大意:有n中化学成分,每种成分要么选取重量Wi,获得Ti的TEV值,要么不取,获得0的TEV值.现在对于每种病 毒,要求配置质量为Mi的药物,并且TEV值为Si,求化学成分组成. 解题思路:看了别人的题解,以前居然不知道背包转移可以用二进制. 因为质量总共才50,所以用一个long long的二进制数表示说哪些质量是可祖长的,这样的话只要开一个20W的数组 s,s[i]表示说TEV值为i时质量可以为多少.然后每次在用lowbit处

HDU 4352 XHXJ&#39;s LIS 数位DP + 状压

由LIS的nlogn解法 可以得出最后统计数组中数的个数即为LIS的长度 这样就可以状压了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <c

2017.8.15 [Haoi2016]字符合并 区间dp+状压dp

[题目描述] 有一个长度为n的01串,你可以每次将相邻的k个字符合并,得到一个新的字符并获得一定分数.得到的新字符和分数由这k个字符确定.你需要求出你能获得的最大分数. [输入格式] 第一行两个整数n,k. 接下来一行长度为n的01串,表示初始串.输入的的相邻字符之间用一个空格隔开. 接下来2k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符, wi表示对应的第i种方案对应获得的分数. [输出格式] 输出一个整数表示答案. [

HDU.4352.XHXJ&#39;s LIS(数位DP 状压 LIS)

题目链接 数位DP. 至于怎么求LIS,因为只有10个数,所以可以参照O(nlogn)求LIS的方法,状压记录状态. 每次加一个数和求LIS一样更新状态.最后状态中1的个数就是LIS的长度. //93MS 3004K #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() typedef long long LL; c

『字符合并 区间dp 状压dp』

字符合并 Description 有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数.得到的新字符和分数由这 k 个字符确定.你需要求出你能获得的最大分数. Input Format 第一行两个整数n,k.接下来一行长度为n的01串,表示初始串. 接下来2^k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应获得的分数. 1<=n<=300,0<

zoj 3812 We Need Medicine (dp 位优化 巧妙记录路径)

We Need Medicine Time Limit: 10 Seconds      Memory Limit: 65536 KB      Special Judge A terrible disease broke out! The disease was caused by a new type of virus, which will lead to lethal lymphoedema symptom. For convenience, it was namedLL virus.

HDU 5045 DP+状压

2014 ACM/ICPC Asia Regional Shanghai Online 给出N个人做M道题的正确率,每道题只能由一个人做出,并且当所有人都做出来且仅做出一道题时,做过题的人才可以继续做题,求最大期望. 一共只有10个人,状压存储每个人是否已经做出题目,如果都作出则状态清0: #include "stdio.h" #include "string.h" double Max(double a,double b) { if (a<b) return

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]