zoukankan      html  css  js  c++  java
  • zoj 3812 状压dp

    转载: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可以直接算出第一种属性的组合情况,枚举一下新增的位,更新一下结果就行了。


    代码:

    #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
    #define Inf 0x3FFFFFFFFFFFFFFFLL
    #define eps 1e-8
    #define pi acos(-1.0)
    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];
    ull f[200010];
    int W[maxn],T[maxn];
    map<ull,int>mp;
    int main()
    {
    //    freopen("in.txt","r",stdin);
    //    freopen("out.txt","w",stdout);
        for(int i = 1;i <= M + 1;++i)
            mp[1LL<<(i-1LL)] = 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;
            ull 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(ull k = v ^ f[j];k ; k &= k-1)               //枚举新增加的集合
                    {
                        x = (k ^ (k - 1)) & k;                      
                        ans[j][mp[x] - 1] = 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;
    }
  • 相关阅读:
    水晶苍蝇拍:到底怎样把握企业的内在价值? (2010-12-29 15:37:11)
    水晶苍蝇拍:个人投资者必须学会扬长避短 (2010-12-24 12:04:59)
    水晶苍蝇拍:公司研究不要“就事论事”而要“逻辑支点” (2010-12-23 10:58:23)
    水晶苍蝇拍:再高的学识也扛不住“浮躁” (2010-12-03 08:31:33)
    水晶苍蝇拍:人生不同阶段的投资规划 (2010-12-01 08:20:13)
    水晶苍蝇拍:好公司的3个判断维度:成长前景-机会程度-生意属性 (2010-11-29 08:32:30)
    水晶苍蝇拍:我为什么不会重仓买入银行股? (2010-11-28 10:20:00)
    水晶苍蝇拍:投资随谈之:关于股价的“溢价与泡沫” (2010-11-25 08:21:01)
    fgets汉字问题
    sizeof('a')
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410611.html
Copyright © 2011-2022 走看看