HDU 4212(预处理,dfs

题目:给出1到N的N张纸牌,一共最多有T轮,每轮需要拿出一定总和的牌的组合,问最后最多能拿出多少牌。

思路:首先应该想到的是预处理出拿出牌的组合,这样每次需要拿出某个数的时候直接从列表里搜索,因为N为22,所以直接dfs枚举。然后就是搜索了,直接写一个dfs搜答案是很容易想到的,但是跑一下会发现即使是样例也很慢。。。实际上这时候只需要稍稍加一点优化就可以过了,我们注意到到达某个状态的时候牌的总和是一定的,反过来,如果已经拿出了某个组合的牌,那么它所在的状态也是确定的。。。拿出牌的方法有2^N种,和预处理的数量是一样的,所以只要加一个vis数组避免状态重复就可以把复杂度限制在2^N。

#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
#define IINF (1<<30)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
int T,N;
int V[30];
vector<int> tab[39];
vector<int> tnum[30];
int vis[1<<22];
void dfs(int val,int v,int mask,int num){
    if(val>22) return;
    if(v==22){
        if(val<=22){
            tab[val].pb(mask);
            tnum[val].pb(num);
        }
        return;
    }
    dfs(val,v+1,mask,num);
    dfs(val+v+1,v+1,mask+(1<<v),num+1);
}
int ans=0;
void work(int p,int mask,int num){
    if(vis[mask]) return;
    vis[mask]=1;
    ans=max(ans,num);
    if(p==T+1) return;
    for(int i=0;i<tab[V[p]].size();i++){
        int v=tab[V[p]][i];
        if(v>=(1<<N)) continue;
        if(!(mask&v)){
            work(p+1,mask+v,num+tnum[V[p]][i]);
        }
    }
}
int cas=0;
int main(){
    /////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    dfs(0,0,0,0);
    while(cin>>N>>T){
        memset(vis,0,sizeof vis);
        if(N==0&&T==0) break;
        for(int i=1;i<=T;i++){
            scanf("%d",V+i);
        }
        ans=0;
        work(1,0,0);
        printf("Game %d: %d\n",++cas,ans);
    }
    return 0;
}
View Code

 

posted @ 2015-08-03 21:13  PlusSeven  阅读(164)  评论(0编辑  收藏  举报