给出一个 n×m n × m 的地图, '.' ′ . ′ 表示空地, 'H' ′ H ′ 表示帽子;
一开始玩家 B B 将 k k 个硬币放在帽子下面,每个帽子最多放一个硬币,每个硬币有一个价值 V V ;
玩家 B B 始终需要满足:
1.每一行的硬币数量加上帽子数量的和为偶数
2.每一列的硬币数量加上帽子数量的和为偶数
玩家 A A 有 numGuesses n u m G u e s s e s 次机会,他每次选择一个帽子,在翻开之前,玩家 B B 可以随意交换两个硬币的位置(玩家 B B 不能再向已经翻开了的帽子里塞硬币)。
翻开帽子后帽子与硬币(如果有的话)仍在原来的位置。
问在玩家 A A 和 B B 表现最佳的情况下,玩家 A A 翻开的帽子中的所有硬币价值之和最大是多少。每一次玩家 B B 都必须藏下所有的硬币。如果一开始玩家 B B 无法将所有硬币藏下,输出 −1 − 1 。
n<13V<10000 n < 13 V < 10000
因为n只有13,显然就是状压dp
可以定义
dp[s] d p [ s ] 表示状态s下的最大硬币数
第 i i 位为 0 0 :第 i i 个帽子没有被翻开;
第 i i 位为 1 1 :第 i i 个帽子被翻开后没有硬币;
第 i i 位为 2 2 :第 i i 个帽子被翻开后有硬币;
那么转移就有:
dp[s]=max(min(dp[第i位变1],dp[第i位变2]+1),其中第i位为0) d p [ s ] = m a x ( m i n ( d p [ 第 i 位 变 1 ] , d p [ 第 i 位 变 2 ] + 1 ) , 其 中 第 i 位 为 0 )
这里个人觉得顺推有点麻烦,而记忆化搜索个人觉得写起来可能更方便。
#define N 13
int n,m,K,num;
char s[N];
int val[N];
int x[N],y[N],tot;
int Pow[N];
int dp[1600000];
bool row[N],col[N];
int dfs(int s,int A,int B){
int &t=dp[s];
if(~t)return t;
if(!A){
mcl(row,0);mcl(col,0);
SREP(i,0,tot)if(s/Pow[i]%3!=2)row[x[i]]^=1,col[y[i]]^=1;
SREP(i,0,n)if(row[i])return t=INF;
SREP(i,0,m)if(col[i])return t=INF;
return t=0;
}
if(!B){
SREP(i,0,tot)if(!(s/Pow[i]%3)&&!dfs(s+2*Pow[i],A-1,0))return t=0;
return t=INF;
}
SREP(i,0,tot)if(!(s/Pow[i]%3))chkmax(t,min(dfs(s+Pow[i],A,B-1),dfs(s+Pow[i]*2,A-1,B-1)+1));
return t;
}
void Init(){
mcl(dp,-1);
Pow[0]=1;
SREP(i,1,N)Pow[i]=Pow[i-1]*3;
}
int main(){
Init();
scanf("%d%d%d%d",&n,&m,&K,&num);
SREP(i,0,n){
scanf("%s",s);
SREP(j,0,m)if(s[j]=='H')x[tot]=i,y[tot++]=j;
}
SREP(i,0,K)scanf("%d",&val[i]);
sort(val,val+K);
int cnt=dfs(0,K,num);
if(cnt==INF)puts("-1");
else {
int ans=0;
SREP(i,0,cnt)ans+=val[i];
printf("%d\n",ans);
}
return 0;
}