[状压DP] TopCoderSRM549 DIV1 600. MagicalHats

24 篇文章 0 订阅
10 篇文章 0 订阅

对帽子状压,0表示没选过,1表示选过但是没有硬币,2表示选过且存在硬币。
可以先dp出每一种情况是否合法,然后dp

显然只要DP出最多能得到几个硬币就好了(magician肯定会从小到大给你硬币)
对于状态S,枚举选择哪个帽子,magician会在“在这个帽子里放硬币”和“在这个帽子里不放硬币”选择较小的。

记忆化搜索一下

// BEGIN CUT HERE  

// END CUT HERE  
#line 5 "MagicalHats.cpp"  
#include <vector>  
#include <list>  
#include <map>  
#include <set>  
#include <deque>  
#include <stack>  
#include <bitset>  
#include <algorithm>  
#include <functional>  
#include <numeric>  
#include <utility>  
#include <sstream>  
#include <iostream>  
#include <iomanip>  
#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <ctime>  
#include <cstring>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;  

int n,m,c,nh;
int f[1594326],g[1594326];
int pw[15];
vector<pair<int,int> > h,cur;

inline int getw(int S,int p){
  return S/pw[p]%3;
}

inline int setw(int S,int p,int x){
  S-=(S/pw[p]%3)*pw[p];
  return S+x*pw[p];
}

int dfs(int S){
  if(~g[S]) return g[S];
  int r=c;
  for(int i=0;i<nh;i++) if(getw(S,i)==2) r--;
  if(r==0){
    int h=0,r=0;
    for(int i=0;i<nh;i++)
      if(getw(S,i)!=2) h^=1<<(::h[i].fi),r^=1<<(::h[i].se);
    return g[S]=(!h && !r);
  }
  g[S]=0;
  for(int i=0;i<nh;i++)
    if(!getw(S,i)) g[S]|=dfs(setw(S,i,2));
  return g[S];
}

int trans[1594326],num[1594326],k;

int dp(int S){
  int r=c,gs=0;
  for(int i=0;i<nh;i++){
    if(getw(S,i)==2) r--;
    if(getw(S,i)) gs++;
  }
  if(gs==k) return 0;
  if(~f[S]) return f[S];
  int &ret=f[S];
  for(int i=0;i<nh;i++)
    if(!getw(S,i)){
      int a=setw(S,i,1),b=setw(S,i,2),cur=1<<30;
      if(!g[a] && !g[b]) continue;
      if(g[a]) cur=min(cur,dp(a));
      if(g[b]) cur=min(cur,dp(b)+1);
      ret=max(ret,cur);
    }
  return ret;
}


class MagicalHats  
{  
public:  
  int findMaximumReward(vector <string> board, vector <int> coins, int numGuesses){
    n=board.size(); m=board[0].size(); c=coins.size(); h.clear(); k=numGuesses;
    memset(f,-1,sizeof(f)); memset(g,-1,sizeof(g));
    pw[0]=1; for(int i=1;i<=13;i++) pw[i]=pw[i-1]*3;
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++)
    if(board[i][j]=='H') h.pb(mp(i,j));
    nh=h.size();
    for(int i=0;i<pw[nh];i++) dfs(i);
    dp(0);
    if(f[0]<0) return -1;
    sort(coins.begin(),coins.end());
    int ret=0;
    for(int i=0;i<f[0];i++) ret+=coins[i];
    return ret;
  }

};  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值