题意:
现在有
个队伍,第一次每相邻两个队伍比赛,然后剩下
个胜利者和
个loser,胜利者会进行下一场比赛,同时loser也会进行下一场比赛,然后对于这次胜利者淘汰下来的
个人,加上loser中的
个人,又进行一场比赛,然后循环这个操作,直到最后一个胜利者和最后一个loser,他们会进行一次决斗。
你有一些喜欢的人,给你这些人的编号,你可以任意指定每一个人每一局的输赢使得所有比赛中包含你喜欢的人出场的比赛最多,问你最多是多少。
题解:
我看了有很多解法,但是我一个都想不出来,我选了记忆化搜索来理解,dp[x][l][r]表示在l到r这个区间最终赢的赢和输的赢的状态为x时答案最大是多少。因为败方比赛会有两次,所以可以直接用2表示败方是否有。
那么状态转移就是两个子区间的和,枚举左区间和右区间的情况。
((i&2)|(i<<1)|j)&3
这里我也理解了一会,这种情况是i的胜败了,i&2表示i的败方是否有喜欢的队伍,i<<1表示i的胜方到败方时是否有喜欢的队伍。
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<17)+5;
unordered_map<int,int>mp[4][N];
bool fav[N];
int dfs(int l,int r,int s){//01:赢得赢 10:输的赢
if(r==l+1){
int num1=fav[l]+fav[r],num2=(s&1)+(s>>1);
if(num1==num2)
return min(1,num2);
else
return -1e6;
}
if(mp[s][l].count(r))
return mp[s][l][r];
int ans=-1e6,mid=l+r>>1;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
int ilose=((i&2)|(i<<1)|j)&3,jlose=((j&2)|(j<<1)|i)&3;
if(ilose==s||jlose==s)
ans=max(ans,dfs(l,mid,i)+dfs(mid+1,r,j));
}
}
return mp[s][l][r]=ans+s;
}
int main()
{
int n,m,x;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&x),fav[x]=1;
int ans=0;
for(int i=0;i<4;i++)
ans=max(ans,dfs(1,1<<n,i));
printf("%d\n",ans+(m?1:0));//最后赢和输还有一场对决
return 0;
}