zoukankan      html  css  js  c++  java
  • 状态压缩DP

    简单题

    1.学校食堂

    https://vijos.org/p/1546

    看起来很简单,结果写了好久好久。我太弱啦。

    dp[i][j][k]表示i以前的都处理好了,j表示 i及i以后的七个人的01处理状态,k表示这个状态是由哪个人转移过来的(1~mm表示i之前的,mm+1以后表示i和i之后的人) 

    一开始感觉很好转移,写起来就出现了很多问题,根本不知道如何预处理?

    后来问了杯哥,发现自己智障地以为i的状态就该向i以后的状态转移,其实i是可以向自己转移的QAQ

    两种转移,一是j未到达全1,向i自己转移,二是j的前b项达到全1就向i+b转移。

    自己写丑了,感觉别人的代码都贼短,不过懒得又抄一遍题解啦,反正自己瞎YY的丑就丑吧

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int M=1000+5;
    const int N=(1<<9);
    int T,n,nn,mm,ti[M],rn[M],dp[M][N][17],no[M][N],cnt[N];
    void init(){
        scanf("%d",&n);
        mm=0;
        for(int i=1;i<=n;i++){
        scanf("%d%d",&ti[i],&rn[i]);
        mm=max(mm,rn[i]);
        } 
        mm++;
    }
    void pre(){
        nn=(1<<mm)-1;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=nn;j++){
                int rr=min(i+mm-1,n),cc=0,xx;
                for(int k=0;k<mm;k++){
                    if(j&(1<<k)){
                        if(i+k>rr) no[i][j]=1;
                        cc++;
                        xx=k+1;
                    }
                    else rr=min(rr,i+k+rn[i+k]);
                }
                cnt[j]=cc;
                if(cnt[j]==1) 
                  dp[1][j][xx+mm]=0;
            }
    }
    void work(){
        for(int i=1;i<=n;i++) {
            for(int k=0;k<=mm;k++) {
                for(int j=0;j<=nn;j++) {
                if(no[i][j]||cnt[j]!=k) continue;
                    for(int l=1;l<=(mm+1)*2;l++) {
                    if(dp[i][j][l]==-1) continue;
                        for(int s=0;s<mm;s++) {
                            if(!(j&(1<<s))) {
                                int now=j|(1<<s);
                                if(no[i][now]) continue;
                                int pr=(l<mm+1)?i-(mm+1-l):i+l-(mm+1);
                                int cost=(ti[pr]|ti[i+s])-(ti[pr]&ti[i+s]);
                                if(dp[i][now][mm+1+s]==-1||dp[i][now][mm+1+s]>dp[i][j][l]+cost)
                                    dp[i][now][mm+1+s]=dp[i][j][l]+cost;
                            
                            }
                        }
                        for(int s=0;s<mm;s++) {
                            if(!(j&(1<<s))) break;
                            int now=j>>s+1;
                            if(dp[i+s+1][now][l-s-1]==-1||dp[i+s+1][now][l-s-1]>dp[i][j][l])
                            dp[i+s+1][now][l-s-1]=dp[i][j][l];
                        }
                    }
                }
            }      
        }
        int ans=1000000000;
        for(int i=1;i<=(mm+1)*2;i++){
            if(dp[n][1][i]!=-1) ans=min(ans,dp[n][1][i]);
        }   
        printf("%d
    ",ans);
    }
    void clear(){
        memset(dp,-1,sizeof(dp));
        memset(no,0,sizeof(no));
    }
    int main()
    {
        scanf("%d",&T);
        while(T--){
            clear();
            init();
            pre();
            work();
        }
        return 0;
    }
    学校食堂

     

    2.炮兵布阵

    https://vijos.org/p/1424

    先预处理出所有不合法的状态,有冲突的和每一行是1的位置为山地的为不合法。

    dp[i][j][k]表示处理到了第i行,它的状态为j,它上一行的状态为k的最优解,转移的时候再判断一下三行直接冲不冲突即可。

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=(1<<10);
    int ans,nn,dp[105][61][61],a[105][20],n,m,zt[65],zts,gs[65],no[105][61];
    char ch[20];
    void pre(){
        nn=((1<<m)-1);
        for(int i=0;i<=nn;i++){
            int pre=-3,tp=i,k=0,v=0;
            while(tp){
                k++;
                if(tp&1) {
                v++;
                if(k-pre<3) break;
                pre=k; 
                }
                tp>>=1;
            }
            if(!tp) {
            zt[++zts]=i;
            gs[zts]=v;
            }
        }
        for(int i=1;i<=zts;i++) dp[1][1][i]=gs[i];
        for(int i=1;i<=zts;i++)
        for(int j=1;j<=m;j++)
        for(int k=1;k<=n;k++)
        if((zt[i]&(1<<(j-1)))&&a[k][j]) no[k][i]=1;
    }
    int check(int x,int y){
        for(int i=1;i<=m;i++) {
            if((zt[x]&(1<<(i-1)))&&((zt[y]&(1<<(i-1))))) return 0;
        }
        return 1;
    }
    int ok(int j,int k,int l){
        if(!check(j,k)) return 0;
        if(!check(k,l)) return 0;
        if(!check(j,l)) return 0;
        return 1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%s",ch);
            for(int j=0;j<m;j++)
                if(ch[j]=='H')
                    a[i][j+1]=1;    
        }
        pre();
        for(int i=2;i<=n;i++)
            for(int j=1;j<=zts;j++) {  //上二行 
                if((i==2&&j!=1)||(no[i-2][j])) continue;
                for(int k=1;k<=zts;k++) { //上一行
                    if(no[i-1][k]) continue;
                    for(int l=1;l<=zts;l++) {  //当前行 
                        if(no[i][l]||!ok(j,k,l)) continue;
                        dp[i][k][l]=max(dp[i][k][l],dp[i-1][j][k]+gs[l]);
                    }
                }
            }
        for(int i=1;i<=zts;i++)
        for(int j=1;j<=zts;j++)
        ans=max(ans,dp[n][i][j]);
        printf("%d
    ",ans);
        return 0;
    }
    炮兵阵地

     

    3.NOIP2016愤怒的小鸟

    很久之前抄的题解,忘得差不多了,先码着

    //Twenty
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    double eps=1e-10;
    int t,n,m,f[20][20],cnt,dp[1<<20];
    double x[20],y[20];
    int min(int a,int b){
        return a<b?a:b;
    } 
    void makebag()
    {
        memset(f,0,sizeof(f));
        for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
        {
            double x1=x[i],x2=x[j],y1=y[i],y2=y[j];
            double a,b;
            a=(y1*x2-y2*x1)/(x1*x2*(x1-x2));
            b=((y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x1*x1*x2));
            
            if(a>=-eps) continue;
            for(int k=i;k<n;k++)
                {
                    if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<=eps)
                      f[i][j]|=(1<<k);
               }    
           
        }
    }
    int dpp()
    {
        memset(dp,127/3,sizeof(dp));
        dp[0]=0;int i;
        for(int v=0;v<(1<<n)-1;v++)
        {
            i=0;
            while(v&(1<<i)) i++;
            dp[v|(1<<i)]=min(dp[v|(1<<i)],dp[v]+1);
            for(int j=i;j<n;j++)
            dp[v|f[i][j]]=min(dp[v|f[i][j]],dp[v]+1);
        }
    }
    
    int main() 
    {
      scanf("%d",&t);
      while(t--){
          scanf("%d%d",&n,&m);
          for(int i=0;i<n;i++)
          scanf("%lf%lf",&x[i],&y[i]);
          makebag();
          dpp();
          printf("%d
    ",dp[(1<<n)-1]);
      }
      return 0;
    }
    angry birds

     

    4.坑人水题 bzoj3446 Cow Decathlon

         题目很水。不过第一次知道了预处理出状态中1的个数来优化转移。

         有个坑点是每个时间段可能有多个特别奖励的条件,调了好久。。

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=1<<20;
    int n,b,cntsp,sp[21][2],v[21][21],x,y,z,nn,dp[21][maxn],bit[maxn]; 
    vector<int>s[22];
    int main()
    {
        scanf("%d%d",&n,&b);
        for(int i=1;i<=b;i++){
            scanf("%d%d%d",&x,&y,&z);
            sp[++cntsp][0]=y;
            sp[cntsp][1]=z;
            s[x].push_back(cntsp);
        }
        nn=(1<<n)-1;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&v[i][j]);
        
        for(int i=0;i<=nn;i++){
            int base=0;
            for(int j=1;j<=n;j++){
             if(i&(1<<(j-1))) base++;
            }
            bit[i]=base;
        }
        
        for(int i=1;i<=n;i++)
        for(int j=0;j<=nn;j++){
            if(bit[j]!=i) continue;
            for(int k=1;k<=n;k++)
             {
                  if(j&((1<<(k-1))))
                  {
                      int now=v[k][i];
                      for(int kd=0;kd<s[i].size();kd++){
                          int xx=sp[s[i][kd]][1],yy=sp[s[i][kd]][0];
                      if(now+dp[i-1][j^(1<<(k-1))]>=yy) 
                        now+=xx;
                      }//坑爹啊 一个点可能有多个特殊价值 
                      dp[i][j]=max(dp[i][j],dp[i-1][j^(1<<(k-1))]+now);
                  }
             }
        }
        printf("%d
    ",dp[n][nn]);
        return 0;
    }
    Cow Decathlon

     

    5.水题 扫雷

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=10000+299;
    int ans,n,a[maxn],dp[maxn][8],cnt[10];
    void pre(){
        for(int i=0;i<8;i++)
            for(int j=0;j<3;j++)
                if(i&(1<<j)) cnt[i]++;
        dp[0][0]=dp[0][1]=1;
    }
    void init(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    }
    void work(){
        for(int i=1;i<=n;i++) {
            for(int j=0;j<8;j++) {
                if(cnt[j]!=a[i]||(i==1&&(j&4))||(i==n&&(j&1))) continue;
                dp[i][j]+=(dp[i-1][j>>1]+dp[i-1][(j>>1)^4]);
                if(i==n) ans+=dp[i][j];
            }
        }
        printf("%d
    ",ans);
    }
    int main() 
    {
        pre();
        init();
        work();
        return 0;
    }
    扫雷

     

    6.vijos 兴奋剂检查

    第一次见这样的题,又抄了题解,算是知道了状态压缩的含义,但对题目给的数据范围和这个压缩方式表示怀疑。

    int has(int a,int b,int c,int d,int e){
        return a*(v[2]+1)*(v[3]+1)*(v[4]+1)*(v[5]+1)+b*(v[3]+1)*(v[4]+1)*(v[5]+1)+c*(v[4]+1)*(v[5]+1)+d*(v[5]+1)+e;                         
    }
    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=5000000+299;
    int ans,n,m,dp[maxn],v[6],val,q[6];
    int has(int a,int b,int c,int d,int e){
        return a*(v[2]+1)*(v[3]+1)*(v[4]+1)*(v[5]+1)+b*(v[3]+1)*(v[4]+1)*(v[5]+1)+c*(v[4]+1)*(v[5]+1)+d*(v[5]+1)+e;                         
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d",&v[i]);
        for(int i=1;i<=n;i++){
            scanf("%d",&val);
            for(int i=1;i<=m;i++) scanf("%d",&q[i]);
            for(int a=v[1];a>=q[1];a--)
                for(int b=v[2];b>=q[2];b--)
                    for(int c=v[3];c>=q[3];c--)
                        for(int d=v[4];d>=q[4];d--)
                            for(int e=v[5];e>=q[5];e--){
                                int x=has(a,b,c,d,e),y=has(a-q[1],b-q[2],c-q[3],d-q[4],e-q[5]);
                                dp[x]=max(dp[x],dp[y]+val);
                                ans=max(ans,dp[x]);
                            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    兴奋剂检查

     

    7.水题 vijos 严厉的班长

    一开始脑残了认为一个非1的数跟它自己互质,喵喵喵?

    然后不是的话就很好做了,我们筛出100以内的质数,发现最大会有16个,因为大于59的没有选1划算。

    知道了这一点就可以直接dp了

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=1<<16;
    int n,prime[18]={0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
    int a[maxn],dp[101][maxn],zx[60],nn,ans=1e9+7;
    void init(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    }
    void pre(){
        for(int i=2;i<=58;i++) {
            int tp=i;
            for(int j=1;j<=16;j++) {
                if(tp==1) break;
                if(tp%prime[j]==0){
                    zx[i]=zx[i]|(1<<(j-1));
                    while(tp%prime[j]==0) tp/=prime[j];
                }
            }
        }
    }
    void work() {
        memset(dp,127,sizeof(dp));
        dp[0][0]=0;
        for(int i=0;i<n;i++)
            for(int j=1;j<=58;j++) 
                for(int k=0;k<maxn;k++) {
                    if(k&zx[j]) continue;
                    dp[i+1][k|zx[j]]=min(dp[i+1][k|zx[j]],dp[i][k]+abs(a[i+1]-j));
                    if(i+1==n) ans=min(ans,dp[i+1][k|zx[j]]);
                }
        printf("%d
    ",ans);
    }
    int main()
    {
        init();
        pre();
        work();
        return 0;
    }
    严厉的班长

     

    8.水题 最小总代价

    感觉没什么存在意义的水题。大概就是体现一下水是生命之源吧。

    顺便vijos上的难度真是一点参考价值没有。

    //Twenty
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int maxn=1<<16;
    int ans=1e9,n,nn,pr,v[20][20],cnt[maxn],dp[maxn][20];
    void pre(){
       nn=(1<<n)-1;
       for(int i=1;i<=nn;i++){
              cnt[i]=0; pr=0; 
           for(int j=0;j<16;j++)
               if(i&(1<<j)) {cnt[i]++; pr=j+1;}
           if(cnt[i]==1) dp[i][pr]=0;
       }
    }
    int main()
    {
        //freopen(".in","r",stdin);
        //freopen(".out","w",stdout);
        memset(dp,127,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&v[i][j]);
        pre();
        for(int i=1;i<=n;i++)
        for(int j=1;j<=nn;j++){
            if(cnt[j]!=i) continue;
            for(int k=1;k<=n;k++){
                if(dp[j][k]==-1) continue;
                for(int l=1;l<=n;l++){
                    if(j&(1<<l-1)) continue;
                    dp[j|(1<<l-1)][l]=min(dp[j|(1<<l-1)][l],dp[j][k]+v[k][l]);
                }
            }
        }
        for(int i=1;i<=n;i++)
        ans=min(ans,dp[nn][i]);
        printf("%d
    ",ans);
        return 0;
    }
    最小总代价

     

    9.洛谷 2915 [USACO08NOV]奶牛混合起来Mixed Up Cows

    水题

    10.BZOJ 1087 [SCOI2005]互不侵犯King

    水题

    中等题

    1.bzoj1294: [SCOI2009]围豆豆Bean

    状压+计算几何

    2.bzoj4572: [Scoi2016]围棋

    轮廓线dp

     

    3.自家测试 A题

    奥妙重重的状压dp。

    较难题

    1.长沙集训 Adore

    2.长沙集训 T4逛公园

    可以模拟退火乱搞

  • 相关阅读:
    单片机八位时钟
    共阴数码管断码与位码
    PCB自己做一个原理图模版
    Mongodb在Linux下的安装和启动和配置
    linux下用phpize给PHP动态添加扩展
    微信支付JS各种调试问题
    秒速插入百万测试数据MYSQL,提供你玩玩大数据!
    金子的PHP之禅(函数篇四)
    linux下面查找某个字符或者文件
    金子的PHP之禅(PHP运算符三)
  • 原文地址:https://www.cnblogs.com/Achenchen/p/7474115.html
Copyright © 2011-2022 走看看