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

 

转载于:https://www.cnblogs.com/Cw-trip/p/4700409.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值