SRM549 Div1 Medium

Magical Hats

题意:

给出一个 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[i1],dp[i2]+1),i0) d p [ s ] = m a x ( m i n ( d p [ 第 i 位 变 1 ] , d p [ 第 i 位 变 2 ] + 1 ) , 其 中 第 i 位 为 0 )
这里个人觉得顺推有点麻烦,而记忆化搜索个人觉得写起来可能更方便。

code:

#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;
}

你可能感兴趣的:(算法,SRM,Div1,Medium)