MagicalHats
作者:辜俊儒
关键词:状压dp
题目简述
给出一个 n∗m n ∗ m 的地图,’.’表示空地,’H’表示帽子;
一开始玩家B将硬币放在帽子下面,每个帽子最多放一个硬币,每个硬币有一个价值;
玩家A有 numGuesses n u m G u e s s e s 次机会,每次选择一个帽子,在翻开之前,玩家B可以随意改变硬币的位置,需要满足每一行的硬币数量加上帽子数量的和为偶数,每一列的硬币数量加上帽子数量的和为偶数。
问在玩家A和B的表现最佳的情况下,玩家A翻开的帽子中的所有硬币价值之和最大是多少。
n,m,numGuesses n , m , n u m G u e s s e s 地图中帽子数量,硬币数量均 <=13 <= 13 <script type="math/tex" id="MathJax-Element-23"><=13</script>;
分析
由于帽子的个数有 13 13 ,可以考虑对帽子进行状压。
算法
用 f[s] f [ s ] 表示在状态 s s 下能获得的最大硬币数量。
第位为 0 0 :第个帽子没有被翻开;
第 i i 位为:第 i i 个帽子被翻开后没有硬币;
第位为 2 2 :第个帽子被翻开后有硬币;
转移:枚举一个没有被翻开的帽子(玩家A表现最佳,取最大值),考虑该帽子处是否需要留有硬币(玩家B表现最佳,取最小值)。
f[s]=max f [ s ] = m a x { min(f[第i为变为1],f[第i位变为2]+1),其中第i位为0 m i n ( f [ 第 i 为 变 为 1 ] , f [ 第 i 位 变 为 2 ] + 1 ) , 其 中 第 i 位 为 0 }
时间复杂度 O(3n×n) O ( 3 n × n )
参考代码
#line 7 "MagicalHats.cpp"
#include<bits/stdc++.h>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
#define pb push_back
#define ll long long
#define inf 0x3f3f3f3f
#define fill(a,b) memset(a,b,sizeof(a))
#define cpy(a,b) memcpy(a,b,sizeof(b))
template<typename T> inline bool MIN(T &a,const T &b) {return a>b? a=b,1:0;}
template<typename T> inline bool MAX(T &a,const T &b) {return a<b? a=b,1:0;}
#define mp make_pair
#define fi first
#define se second
#define N
#define mod 1000000007
typedef vector<int> vi;
typedef pair<int,int> pii;
int nG;
struct node {
int x,y;
};
vector<node> q;
int f[1600000],n,power[15];
//f represents the maximum number of coins you can get
//use 3-based
int outbit(int p,int v) {
return v*power[p];
}
int bit(int s,int p) {
return s/power[p]%3;
}
int A[13],B[13];
int get_f(int s,int use,int coin) {
int &res=f[s];
if (res!=-1) return res;
if (!coin) {
fill(A,0),fill(B,0);
for (int i=0;i<n;++i) if (bit(s,i)!=2) A[q[i].x]^=1,B[q[i].y]^=1;
for (int i=0;i<13;++i) if (A[i]||B[i]) return res=inf;
return res=0;
}
if (!use) {
for (int i=0;i<n;++i) if (bit(s,i)==0) {
if (get_f(s+outbit(i,2),0,coin-1)==0) return res=0;
}
return res=inf;
}
for (int i=0;i<n;++i) if (bit(s,i)==0) {
MAX(res,min(get_f(s+outbit(i,1),use-1,coin),get_f(s+outbit(i,2),use-1,coin-1)+1));
//get_f(s+outbit(i,1),use-1,coin) if not place a coin here
//get_f(s+outbit(i,2),use-1,coin-1)+1 if place a coin here
}
return res;
}
class MagicalHats
{
public:
int findMaximumReward(vector <string> board, vector <int> coins, int numGuesses)
{
nG=numGuesses;
power[0]=1; for (int i=1;i<15;++i) power[i]=power[i-1]*3;
int r=board.size(),c=board[0].size();
fill(f,-1);
q.clear();
for (int i=0;i<r;++i) {
for (int j=0;j<c;++j) {
if (board[i][j]=='H') q.pb((node){i,j});
}
}
n=q.size();
int t=get_f(0,numGuesses,coins.size());
if (t>100) return -1;
int ans=0;
sort(coins.begin(),coins.end());
for (int i=0;i<t;++i) ans+=coins[i];
return ans;
}
};
记忆化搜索 by tourist
class MagicalHats {
public:
int findMaximumReward(vector <string>, vector <int>, int);
};
int k, x[15], y[15], guess, kg;
int mem[2222222];
int good[222222];
int go(int v) {
if (mem[v] >= 0) return mem[v];
int b[15], vv = v, cur = 0;
for (int i=0;i<k;i++) {
b[i] = vv % 3;
if (b[i] > 0) cur++;
vv /= 3;
}
if (cur == guess) {
mem[v] = 0;
for (int i=0;i<k;i++)
if (b[i] == 1) mem[v]++;
return mem[v];
}
int mx = 0;
for (int i=0;i<k;i++) {
if (b[i] > 0) continue;
int mn = 100000;
for (int j=1;j<=2;j++) {
int canbe = 0;
for (int tt=0;tt<kg;tt++) {
int t = good[tt];
int ok = 1;
for (int z=0;z<k;z++)
if (b[z] > 0)
if (t & (1 << z)) {
if (b[z] == 2) {ok = 0; break; }
} else {
if (b[z] == 1) {ok = 0; break; }
}
if (!ok) continue;
if (j == 1) {
if (t & (1 << i)) {
canbe = 1;
break;
}
} else {
if (!(t & (1 << i))) {
canbe = 1;
break;
}
}
}
if (canbe) {
vv = v;
int pw = 1;
for (int z=0;z<i;z++) pw *= 3;
pw *= j;
if (go(vv + pw) < mn) mn = go(vv+pw);
}
}
if (mn == 100000) continue;
if (mn > mx) mx = mn;
}
mem[v] = mx;
return mx;
}
int MagicalHats::findMaximumReward(vector <string> a, vector <int> coins, int guess) {
int n = a.size(), m = a[0].length(), i, j, k = 0;
int row[16], col[16];
::guess = guess;
for (i=0;i<n;i++)
for (j=0;j<m;j++)
if (a[i][j] == 'H') {
x[k] = i;
y[k] = j;
k++;
}
::k = k;
kg = 0;
for (int t=0;t<(1<<k);t++) {
int kq = 0, p[111];
for (i=0;i<k;i++)
if (t & (1 << i)) p[kq++] = i;
if (kq != coins.size()) continue;
for (i=0;i<n;i++) row[i] = 0;
for (i=0;i<m;i++) col[i] = 0;
for (i=0;i<n;i++)
for (j=0;j<m;j++)
if (a[i][j] == 'H') {
row[i] ^= 1;
col[j] ^= 1;
}
for (i=0;i<kq;i++) {
row[x[p[i]]] ^= 1;
col[y[p[i]]] ^= 1;
}
int ok = 1;
for (i=0;i<n;i++)
if (row[i]) ok = 0;
for (i=0;i<m;i++)
if (col[i]) ok = 0;
if (!ok) continue;
good[kg++] = t;
}
if (kg == 0) return -1;
int pw = 1;
for (i=0;i<k;i++) pw *= 3;
for (i=0;i<pw;i++) mem[i] = -1;
int cnt = go(0);
sort(coins.begin(),coins.end());
int ans = 0;
for (i=0;i<cnt;i++) ans += coins[i];
return ans;
}