传送门
解题思路:dp[i][j]代表到第i行的第j个状态所能得到的最大的和,可以先处理一行没有两个相邻的满足条件的状态。然后把每行每个状态的和都算出来,状态转移的条件是当前这行第j个状态与上一行的第k个状态想与为0,即没有相邻的,那就能更新dp[i][j]:dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[i][j]);还有一个就是n为20的最大情况数,不能直接1<<20这样会爆内存,不晓得怎么用数学直接算出来了,那就只能暴力一下了,在17000+,开20000或者1<<15都够了。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define sz(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=1<<15;
int n,mp[21][21];
int dp[21][maxn],sum[21][maxn];
int sta[maxn]; //记录所有可能的状态
il int cal(int id,int state) { //第id层状态为state的和
int sum=0;
rep(i,0,n-1) {
if((state>>i)&1) sum+=mp[id][n-1-i];
}
return sum;
}
int main() {
std::ios::sync_with_stdio(0);
while(cin>>n) {
ms(dp,0),ms(sum,0);
rep(i,0,n-1) rep(j,0,n-1) cin>>mp[i][j];
int cnt=0;
for(int i=0; i<(1<<n); ++i) {
if(i&(i>>1)) continue;
sta[cnt++]=i;
}
for(int i=0; i<n; ++i) {
for(int j=0; j<cnt; ++j) {
sum[i][j]=cal(i,sta[j]);
}
}
for(int i=0; i<cnt; ++i) dp[0][i]=sum[0][i];
for(int i=1; i<n; ++i) { //到i行
for(int j=0; j<cnt; ++j) { //本行状态
for(int k=0; k<cnt; ++k) { //上一行的状态
if((sta[j]&sta[k])==0) {
dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[i][j]);
}
}
}
}
int ans=0;
for(int i=0; i<cnt; ++i) ans=max(ans,dp[n-1][i]);
cout<<ans<<endl;
}
return 0;
}