//f[i,j,f1,f2] //f1 和 f2 都是01数 //1表示剩下的人是喜欢的 //从j开始,长度为2^i个人,胜者组为f1,败者组为f2 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = ((1<<17)+1000); int n,k,fans[maxn]; int dp[18][maxn][2][2]; //初始化 void init() { for(int i=0; i<18; i++) { for(int j=0; j<maxn; j++) { for(int k=0; k<2; k++) { for(int z=0; z<2; z++) { dp[i][j][k][z]=-1e9; } } } } } int main() { init(); cin>>n>>k; for(int i=0; i<k; i++) { int x; cin>>x; fans[x-1]=1; } //枚举i for(int i=1; i<=n; i++) { //枚举人 区间长度 for(int j=0; j<(1<<n); j+=(1<<i)) { //等于1,也就是初始化的时候 if(i==1) { //表示状态 // 比如说 1 和 2 //可以1 胜 2 败 //也可以2 胜 1 败 for(int x1=0; x1<2; x1++) { for(int y1=0; y1<2; y1++) { // if(x1+y1==fans[j]+fans[j+1]) { //只要有一个是喜欢的,那么就有一场比赛 dp[i][j][x1][y1]=((fans[j]+fans[j+1])>0?1:0); } } } } else { //进行状态转移 合并 //第一个胜者组状况和败者组状况 for(int x1=0; x1<2; x1++) { for(int y1=0; y1<2; y1++) { //第二个胜者组状况和败者组状况 for(int x2=0; x2<2; x2++) { for(int y2=0; y2<2; y2++) { //一定是x1和x2比赛 //y1和y2比赛 //x1和x2比赛,要么x1输,要么x2输 //y1和y2比赛,要么y1输,要么y2输 //然后x1或者x2掉下来,再比赛 //那么一个就是八种 //原来两种的方案 int cost=dp[i-1][j][x1][y1]+dp[i-1][j+(1<<(i-1))][x2][y2]; //胜者组 if(x1||x2) cost++; //败者组 if(y1||y2) cost++; //直接暴力枚举这八种转移 // dp[i][j][x1][x2]=max(dp[i][j][x1][x2],cost+((x2+y1)>0?1:0)); dp[i][j][x1][y1]=max(dp[i][j][x1][y1],cost+((x2+y1)>0?1:0)); dp[i][j][x1][x2]=max(dp[i][j][x1][x2],cost+((x2+y2)>0?1:0)); dp[i][j][x1][y2]=max(dp[i][j][x1][y2],cost+((x2+y2)>0?1:0)); dp[i][j][x2][x1]=max(dp[i][j][x2][x1],cost+((x1+y2)>0?1:0)); dp[i][j][x2][y2]=max(dp[i][j][x2][y2],cost+((x1+y2)>0?1:0)); dp[i][j][x2][x1]=max(dp[i][j][x2][x1],cost+((x1+y1)>0?1:0)); dp[i][j][x2][y1]=max(dp[i][j][x2][y1],cost+((x1+y1)>0?1:0)); } } } } } } } int ans = 0; for(int i=0; i<2; i++) { for(int j=0; j<2; j++) { int cost=dp[n][0][i][j]; //再判断决赛,是否多看一场 if(i+j>0) cost++; ans=max(ans,cost); } } cout<<ans<<endl; }
来源:https://www.cnblogs.com/QingyuYYYYY/p/12501716.html