HDU - 1565 方格取数 (状压dp)

题目链接


题目大意:
一个n*n的格子的棋盘,每个格子里面有一个非负数。从中取出若干个数,使得任意的两个数所在的格子没有公共边,并且取出的数的和最大。
解题思路:
因为两个数之间不能相邻,即每行用状态表示出来,1表示取,0表示不取,所以状态方程为dp[i][j]=max(dp[i][j],dp[i-1][k]+val);(第一维表示行数,第二维表示行的状态)当(sta[j]&sta[k])==0时,我们可以先用打表的方式求得状态表示。
代码:
#include
#define pb emplace_back
#define LOCAL
  
using namespace std;
  
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+5;
typedef long long ll;
typedef pair<int,int> Pii;
 
template <typename T>inline void read(T& t){
    char c=getchar();t=0;
    int f=1;
    while(!isdigit(c)){
       if(c=='-')f=-1;
        c=getchar();
    }
    while(isdigit(c))t=t*10+c-48,c=getchar();
    t=f*t;
}
  
template <typename T,typename... Args> inline void read(T& t,Args&... args){
    read(t);read(args...);
}

int tot=0;
int sta[maxn];
int a[25][25];

int ksm(int i,int sta){
    int ans=0,res=1;
    while(sta){
        if(sta&1){
            ans+=a[i][res];
        }
        res++;
        sta>>=1;
    }
    return ans;
}
  
int main(){
    int n;
    while(cin>>n){
        tot=0;
        for(int i=0;i<(1<<n);i++){
            if((i&(i>>1))==0){
                sta[tot++]=i;//每增加一列状态,如果都与前面取过得数不相邻,则存入状态
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                read(a[i][j]);
            }
        }
        vector<vector<int>>dp(n+1,vector<int>((1<<n),0));
        for(int i=1;i<=n;i++){
            for(int j=0;j<tot;j++){
                for(int k=0;k<tot;k++){
                    if(!(sta[j]&sta[k])){
                        int val=ksm(i,sta[j]);
                        dp[i][j]=max(dp[i][j],dp[i-1][k]+val);
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<tot;i++){
            ans=max(ans,dp[n][i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(状压dp)