ZOJ Problem Set - 3812 牡丹江网络赛D题 状态压缩 背包

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!

  题意很简单,给你N组数,每组有W和T两个值,Q个询问,是否存在几组数它们W的和是M,Q的和是S。如果存在随便输出一组,否则输出No solution。

  记得当时就按一维背包的方法瞎做,各种超时。。看了题解才知道有这么神奇的方法。因为M才50,就用dp[i]表示T的和为i时W和的状态,也就是2^i为1时说明能达到W的和为i-1。这样关键代码就是这一句dp[j]|=(dp[j-t[i]]<<w[i]),把现在状态左移w[i]说明原来能达到的值都多了w[i]。更新前的状态异或上更新后的状态就是新增的状态,(S^(S-1))&S就是取这个状态最低位的1。             

  for(ULL S=tmp^dp[j];S;S&=(S-1)){
                    ULL k=(S^(S-1))&S;
                    ans[j][mp[k]-1]=i;
                }

这个就可以记录路径了。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<algorithm>
#define INF 0x3f3f3f3f
#define eps 1e-9
#define MAXN 200010
#define MAXM 60
#define MAXNODE 105
#define MOD 100000
#define SIGMA_SIZE 4
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
int T,N,Q,w[410],t[410];
int ans[MAXN][MAXM];
ULL dp[MAXN];
map<ULL,int>mp;
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    for(int i=0;i<55;i++) mp[1ULL<<i]=i+1;
    while(T--){
        scanf("%d%d",&N,&Q);
        memset(dp,0,sizeof(dp));
        memset(ans,0,sizeof(ans));
        dp[0]=1;
        for(int i=1;i<=N;i++){
            scanf("%d%d",&w[i],&t[i]);
            for(int j=MAXN-1;j>=t[i];j--){
                ULL tmp=dp[j];
                dp[j]|=(dp[j-t[i]]<<w[i]);
                for(ULL S=tmp^dp[j];S;S&=(S-1)){
                    ULL k=(S^(S-1))&S;
                    ans[j][mp[k]-1]=i;
                }
            }
        }
        int m,s;
        while(Q--){
            scanf("%d%d",&m,&s);
            if(!ans[s][m]){
                printf("No solution!\n");
                continue;
            }
            int first=1;
            while(ans[s][m]){
                if(first) first=0;
                else printf(" ");
                int p=ans[s][m];
                printf("%d",p);
                m-=w[p];
                s-=t[p];
            }
            puts("");
        }

    }
    return 0;
}



你可能感兴趣的:(ZOJ Problem Set - 3812 牡丹江网络赛D题 状态压缩 背包)