题目:给出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 ",++cas,ans); } return 0; }