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.

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 exactWi milligrams in a medicine, or not use it. Each selected substance will addTi 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 toMi 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) andQ (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!

题意:

给你一些材料来配置药品,每种材料有一定的重量和药效,有q个询问,问重量为m,药效为s的药能否配出来。

思路:

一看就觉得是二维费用的背包,但是数据量太大,接受不了,所以要针对问题找另外的解决办法,考虑到重量都在50之内,想到状压。

dp[i][j]表示处理完前i个物品,药效为j时能达到的重量的状态,第一维可以优化。

有点麻烦的是记录路径,因为400*200000的数组开不下,所以得找额外的办法,因为一条路径最多只有50的长度,所以可以用pre[j][k]记录到达药效为j时重量为k时放的是哪一个物品,根据第i个物品递推时会增加的一些状态来记录。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#define maxn 401
#define MAXN 200005
#define INF 0x3f3f3f3f
#define mod 1000000007
#define pi acos(-1.0)
#define eps 1e-6
typedef unsigned long long ull;
using namespace std;

int n,m,u,v,ma;
int w[maxn],t[maxn];
short pre[200001][51];
ull dp[200001];

void solve()
{
    int i,j;
    memset(pre,0,sizeof(pre));
    memset(dp,0,sizeof(dp));
    dp[0]=1;
    ma=200000;
    ull tmp,k,tot=(1ULL<<51)-1;
    for(i=1;i<=n;i++)
    {
        for(j=ma;j>=t[i];j--)
        {
            if(dp[j-t[i]]==0) continue ;
            tmp=dp[j];
            dp[j]|=(dp[j-t[i]]<<w[i])&tot;
            for(k=tmp^dp[j];k;k=(k-1)&k)  // 新增加的状态 一位一位取出
            {
                pre[j][__builtin_ctzll(k)]=i;  // builtin_ctzll-末尾有几个0
            }
        }
    }
}
int main()
{
    int i,j,test,tu;
    scanf("%d",&test);
    while(test--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&w[i],&t[i]);
        }
        solve();
        while(m--)
        {
            scanf("%d%d",&u,&v);
            if(dp[v]&(1ULL<<u))
            {
                printf("%d",pre[v][u]);
                tu=u;
                u-=w[pre[v][u]]; v-=t[pre[v][tu]];
                while(u)
                {
                    printf(" %d",pre[v][u]);
                    tu=u;
                    u-=w[pre[v][u]]; v-=t[pre[v][tu]];
                }
                puts("");
            }
            else printf("No solution!\n");
        }
    }
    return 0;
}
时间: 2024-10-12 15:16:50

zoj 3812 We Need Medicine (dp 位优化 巧妙记录路径)的相关文章

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                                                   

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处

hdu1074 状压DP、栈实现记录路径

题意:给了几门学科作业.它们的截止提交期限(天数).它们的需要完成的时间(天数),每项作业在截止日期后每拖延一天扣一学分,算最少扣的学分和其完成顺序. 一开始做的时候,只是听说过状态压缩这个神奇的东西,但事实上我并不会用它,所以白白想了一个晚上没想出来,然后就看了一下题解```再见吧朋友又是新的算法要学了. 状态压缩,实际上就是用二进制的方式,对于每一个要考察的状态用0/1表示其完成与否,这样当从 1 遍历到 111```111 的时候,就可以遍历完所有的状态了,在遍历的过程中利用位运算以及状态

ZOJ 3812 We Need Medicine 01背包+位优化

题目链接:点击打开链接 #include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <map> typedef unsigned long long ll; using namespace std; const int N = 400 + 1; const int M = 200000 + 1; int t[N], w[N], g[5

ZOJ 3812 We Need Medicine(dp、背包、状态压缩、路径记录)

参考:http://blog.csdn.net/qian99/article/details/39138329 参考的链接里说明得很好,注释也很好...thanks for sharing 朴素的想法不难,dp[i][j][k]类似背包做法即可. 但朴素思想复杂度过高. 这里主要是用到 dif 那个变量,只枚举新增的集合. #include <cstdio> #include <cstring> #include <iostream> #include <algo

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]

ZOJ 3690 Choosing number(dp矩阵优化)

Choosing number Time Limit: 2 Seconds      Memory Limit: 65536 KB There are n people standing in a row. And There are m numbers, 1.2...m. Every one should choose a number. But if two persons standing adjacent to each other choose the same number, the

hdu1224 dp(dp + 栈/父亲数组记录路径)

题意:给定 n 个城市的有趣度,并给出可以从那些城市飞到那些城市.其中第一个城市即起始城市同样也作为终点城市,有趣度为 0,旅行途中只允许按有趣度从低到高旅行,问旅行的有趣度最大是多少,并输出旅行路径. 我一开始读题的时候各种深井冰理解错想复杂,导致我一开始甚至认为第一个有趣度 0 代表的是第二个城市,因为第一个城市一定是 0 不需要给出,加上没看到题意里的第一个城市也是第 n + 1 个城市,觉得给出的能旅行的城市里出现了 n + 1 是应该的.还有关于城市联通,我以为给出的联通关系不一定就是