zoukankan      html  css  js  c++  java
  • 状压dp 练习

    参考雨巨视频:https://www.bilibili.com/video/BV1z441137v6?p=1

    1.互不侵犯

      题目传送门

      参考博客:https://www.cnblogs.com/ljy-endl/p/11627018.html     //tql

     

     

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int n,K,m;
    ll f[17][155][155],ans=0;
    int num[155],s[155];
    int s0=0;
    // f[i][j][k]表示第i行 ,状态为 j ,已经用了 k 个国王所产生的方案数 
    void pre(){//预处理 
        int i,j,k; 
        memset(f,0,sizeof(f));
        for(int i=0;i<m;++i){
            if(i&(i<<1)) continue;    //两个国王相邻(会打起来 例: 1001101 
            k=0;
            for(int j=0;j<n;++j) if(i&(1<<j)) k++; //记录方案有几个国王 
            s[++s0]=i;    //预处理有效的方案 
            num[s0]=k;    //保存每一种方案可放国王数量 
        }
    }
    void dp(){
        f[0][1][0]=1;    //第0行放0个国王的方案数为1 
        for(int i=1;i<=n;++i){    //枚举行 
            for(int j=1;j<=s0;++j){    //枚举可行状态 
                for(int k=0;k<=K;++k){    //枚举国王数量 
                    if(k<num[j]) continue;    
                    for(int t=1;t<=s0;++t){    //枚举上一行的状态 
                        // s[t]&s[j] :s[t]至少有一个在国王在s[j]上面 例:s[t]:  100010001 s[j]: 001010001
                        // s[t]&(s[j]<<1): s[t]至少有一个在国王在s[j]右上方 例:s[t]:  100010001 s[j]: 100100001
                        // s[t]&(s[j]<<1): s[t]至少有一个在国王在s[j]左上方 例:s[t]:  100010001 s[j]: 100001001
                        if((!(s[t]&s[j]))&&(!(s[t]&(s[j]<<1)))&&(!(s[t]&(s[j]>>1))))
                            f[i][j][k]+=f[i-1][t][k-num[j]];
                            //不冲突,加上 上一行 t状态下的[k-num[j]]个国王的方案数 
                    }
                }
            }
        }
        for(int i=1;i<=s0;++i) ans+=f[n][i][K];    //累加统计 
        cout<<ans<<"
    ";
    }
    int main(){
        cin>>n>>K;
        m=(1<<n);  
        pre();
        dp();
        
        return 0;
    }

    2.No Change

      题目传送门

          参考博客:https://ac.nowcoder.com/discuss/633882

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int qs=1e5+7;
    const int inf =0x3f3f3f3f;
    int n,m,s[qs],sum,con[20],f[qs];
    //f[i]表示 状态i下可以买到的物品的数量 
    int Find(int x,int k){//二分找到可以买到的最大价值的下标 
        int l=x,r=m,ans=x;
        while(l<=r){
            int mid=((l+r)>>1);
            if(s[mid]-s[x]<=k) ans=mid,l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
    
    int cal(int x){    //状态x下每个1的位置代表用掉的硬币下标 
        int p=sum;
        for(int i=0;i<n;++i){
            if((x>>i)&1) p-=con[i];
        }
        return p;
    }
    
    int main(){
        cin>>n>>m;
        sum=0;
        for(int i=0;i<n;++i) cin>>con[i],sum+=con[i];
        //统计前缀和,用来二分取区间 
        for(int i=1;i<=m;++i) cin>>s[i],s[i]+=s[i-1]; 
        for(int i=0;i<(1<<n);++i) f[i]=-inf; f[0]=0;
        for(int i=0;i<(1<<n);++i){ //枚举(1<<n)种状态 
            for(int j=0;j<n;++j){    //枚举n枚硬币 
                //在i状态下没有使用j硬币 
                if(!((i>>j)&1)) continue;
                if(f[i-(1<<j)]>=0)
                //找到过去状态,计算购买东西数量 
                    f[i]=max(f[i],Find(f[i-(1<<j)],con[j]));
            }
        }
        int ans=-1;
        for(int i=0;i<(1<<n);++i){
            if(f[i]==m) ans=max(ans,cal(i));
        }
        cout<<ans<<'
    ';
        return 0;
    }

    //理解了一整天 划水了一整天

    //雨巨讲的太好了,我竟然也能理解QAQ

  • 相关阅读:
    待你长发及腰
    《线段树》讲稿
    Codeforces #Round 376 F 题解
    包裹快递 题解
    Codeforces #Round 376 部分题解
    圆圈舞蹈 题解
    奶牛晒衣服 题解
    BZOJ 1034 题解
    BZOJ 1045 题解
    BZOJ 1054 题解
  • 原文地址:https://www.cnblogs.com/Suki-Sugar/p/14635106.html
Copyright © 2011-2022 走看看