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 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

题意:给定n种材料,每种材料w毫克,有t的价值。现在要求用这些材料合成q种新的药品,每种材料或者选一份,或者不选,要求选出的材料重量恰好为m毫克,价值恰好为s。要求输出每种药品是否能合成,若可行要输出任意一种选取方案。这里n,q<=400, w,m<=50, t,s<=200000

思路:开一个长整型数组 f[200005], 其中f[i]的第j位(末尾是0位)表示是否可以选择材料使得其重量和为j而价值和为i。这样就可以用位运算做次DP快速求解了。当然这样只是求出了某种状态是否可行,至于具体方案,还需要多开一个数组ans[200010][52],其中ans[i][j]表示达到这个状态之前需要加入的材料编号,只用在对每种材料DP时对那些从0变1的位置更新就好了(所以下面的代码会有取异或的过程)。注意到这个二维数组中每一项只用更新一次就够了

以上摘抄自2位大神   我选取了最好理解的

for(ull k = v ^ f[j];k ; k &= k-1) //枚举新增加的集合
{
      x = (k ^ (k - 1)) & k;
      ans[j][mp[x] - 1] = i; //将新增的位置更新,记录是使用了哪个物品达到的这个状态
}

这段代码我理解了一个小时才理解......太弱了         其实就是枚举新加的集合,这写法着实神奇

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 400 + 10;
const int M = 51;
short ans[200010][52];
ll f[200010];
int W[maxn],T[maxn];
map<ll,int>mp;
int main()
{
    for(int i = 1;i <= M + 1;++i)
    {
        mp[1LL<<(i)] = i;
    }
    int t,n,q;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        memset(ans,0,sizeof(ans));
        memset(f,0,sizeof(f));
        f[0] = 1;
        ll v,x;
        for(int i = 1;i <= n;++i)
        {
            scanf("%d%d",&W[i],&T[i]);
            for(int j = 200000;j >= T[i];--j)
            {
                v = f[j];                        //f[j]表示第二个属性为j时,能够凑出的第一个属性的集合,用一个二进制数表示,第i位为1表示可以凑出这个数
                f[j] |= (f[j - T[i]]<<W[i]) & ((1LL<<M+1) - 1);          //计算使用当前物品能够得到的新的集合,在集合f[j - T[i]]添加W[i]的物品,
                                                                         //即原来能得到的每个值加上W[i],等价于将其左移W[i]位
                for(ll k = v ^ f[j];k ; k &= k-1)               //枚举新增加的集合
                {
                    x = (k ^ (k - 1)) & k;
                    ans[j][mp[x]] = i;                       //将新增的位置更新,记录是使用了哪个物品达到的这个状态
                }
            }
        }
        int m,s,p;
        for(int i = 0;i < q;++i)
        {
            scanf("%d%d",&m,&s);
            if(!ans[s][m])
                puts("No solution!");
            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];
                }
                puts("");
            }
        }
    }
    return 0;
}
View Code

 

posted @ 2015-09-01 15:41  Doli  阅读(201)  评论(0编辑  收藏  举报