状压DP

状压DP

//一维dp:糖果【蓝桥杯省赛】
# include <bits/stdc++.h>
using namespace std;

const int MAXN=1<<21;
int dp[MAXN],s[110];
int main()
{
   memset(dp,-1,sizeof(dp));
   int N,M,K; scanf("%d%d%d",&N,&M,&K);
   for(int i=1;i<=N;++i){
       int ss=0,t=0;
       for(int j=1;j<=K;++j){
           scanf("%d",&t);
           ss|=(1<<(t-1));   //将每一包糖果转化为可表述
      }
       s[i]=ss;
       //cout<<"ss="<<ss<<endl;
       dp[ss]=1;
  }
   for(int i=1;i<=N;++i){
       for(int j=0;j<(1<<M);++j){   //对每一个现有状态加上一包
           if(dp[j]==-1) continue;
           if(dp[j|s[i]]==-1) dp[j|s[i]]=dp[j]+dp[s[i]];
           else dp[j|s[i]]=min(dp[j|s[i]],dp[j]+dp[s[i]]);
      }
  }
   printf("%d\n",dp[(1<<M)-1]);

   return 0;
}
//二维dp:Mondriaan's Dream
# include <bits/stdc++.h>
using namespace std;

typedef long long LL;
int N,M;
int in[1<<12];
LL f[20][1<<12];
int main()
{
   while(~scanf("%d%d",&N,&M)){
       if(N==0&&M==0) break;
       memset(in,0,sizeof(in));
       memset(f,0,sizeof(f));

       for(int i=0;i<(1<<M);++i){ //0 ~ 2^M-1 每一段连续的0都是偶数个
           int odd=0,cnt=0;
           for(int j=0;j<M;++j){
               if((i>>j)&1) odd|=cnt,cnt=0;
               else cnt^=1;
          }
           in[i]=odd|cnt?0:1;
      }

       f[0][0]=1;
       for(int i=1;i<=N;++i){
           for(int j=0;j<(1<<M);++j){
               for(int k=0;k<(1<<M);++k){ //将条件化为位运算
                   if((j&k)==0&&in[k|j]) f[i][k]+=f[i-1][j];
              }
          }
      }

       printf("%lld\n",f[N][0]);
  }

   return 0;
}
//方格取数
# include <bits/stdc++.h>
using namespace std;

int G[20][20];
int f[20][1<<19];
int is[1<<20];
int N;
int cal(int x,int s)
{
   int ans=0;
   for(int i=N-1;i>=0;--i){
       if(s&1){
           ans+=G[x][i];
           //cout<<"x="<<x<<" "<<G[x][i]<<endl;
      }
       s>>=1;
  }
   return ans;
}
int main()
{
   while(~scanf("%d",&N)){
       memset(f,0,sizeof(f));
       memset(is,0,sizeof(is));
       for(int i=0;i<N;++i){
           for(int j=0;j<N;++j){
               scanf("%d",&G[i][j]);
          }
      }
       int cnt=0;
       for(int i=0;i<(1<<N);++i){
           if(!(i&(i>>1))) is[cnt++]=i;
      }
       for(int i=0;i<N;++i){
           for(int j=0;j<cnt;++j){
               int val=cal(i,is[j]);
               for(int k=0;k<cnt;++k){
                   if(!(is[j]&is[k])){
                       f[i][is[j]]=max(f[i][is[j]],f[i-1][is[k]]+val);
                  }
              }
          }
      }
       int ans=0;
       for(int i=0;i<cnt;++i) ans=max(ans,f[N-1][is[i]]);
       printf("%d\n",ans);
  }

   return 0;
}
//三维dp:炮兵阵地
# include <bits/stdc++.h>
using namespace std;

int N,M;
char mp[110][20];
int f[110][1<<11][1<<11];
int is[1<<11],hi[110];
int num[1<<11];
int lowbit(int x){ return x&(-x); }
int main()
{
   scanf("%d%d",&N,&M);
   for(int i=1;i<=N;++i){
       scanf("%s",mp[i]);
  }
   int cnt=0;
   for(int i=0;i<(1<<M);++i){
       if(!(i&(i>>2))&&!(i&(i>>1))) is[cnt++]=i;
  }
   for(int i=1;i<(1<<M);++i){
       num[i]=num[i-lowbit(i)]+1;
  }
   int ans=0;
   for(int i=1;i<=N;++i){
       for(int j=0;j<M;++j){
           hi[i]|=(mp[i][j]=='H')<<j;
      }
       for(int j=0;j<cnt;++j){  //第i层
           if(is[j]&hi[i]) continue;
           int val=num[is[j]];
           for(int k=0;k<cnt;++k){   //第i-1层
               if(i>=1&&(is[k]&hi[i-1])) continue;
               if(is[k]&is[j]) continue;
               for(int l=0;l<cnt;++l){   //第i-2层
                   if(i>=2&&(is[l]&hi[i-2])) continue;
                   if(!(is[l]&is[k])&&!(is[l]&is[j])){
                       f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+val);
                  }
              }
               //cout<<ans<<endl;
               ans=max(ans,f[i][j][k]);
          }
      }
  }
   printf("%d\n",ans);

   return 0;
}

1、利用位操作表示元素之间的关系

2、元素之间的相邻关系有多少个,就是几维的

3、阶段之间转移,要求状态不冲突

4、若是方格问题,可以先预先处理除同一行的可行的状态,然后在阶段转移中,处理不同行之间的状态关系。若还有关于单元是否选择的问题,可以预先处理出来,(一般以每一行的状态出现),作为另外一个条件在阶段转移的时候进行判断。



posted @ 2022-02-26 23:05  fengzlj  阅读(134)  评论(0)    收藏  举报