bzoj3139: [Hnoi2013]比赛
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3139
思路:记忆化搜索
首先每个队最多只会得27分,27^10还是没有炸longlong的,所以可以用hash存下来。
每个队没有本质区别,所以每层搜索前先排好序,再来一层搜索枚举出当前队和剩余队的输赢情况,转化成子问题继续递归处理。
据说状态数很多,但还是可以接受的。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=12,mod=37779,bas=23,maxt=1500010,pp=1000000007; typedef long long ll; using namespace std; int n,a[maxn],tim; struct hash{ int pre[maxt],now[100010],tot;ll val[maxt],sta[maxt]; ll find(ll x){ int u=x%mod; for (int y=now[u];y;y=pre[y]) if (sta[y]==x) return val[y]; return -1; } void add(ll x,ll cnt){int u=x%mod;pre[++tot]=now[u],now[u]=tot,sta[tot]=x,val[tot]=cnt;} }h; //void print(int a[]){for (int i=0;i<=n;i++) printf("%d ",a[i]);puts("");} ll encode(int a[]){ ll res=0; for (int i=0;i<=n;i++) res=res*30+a[i]; return res; } void decode(int a[],ll x){for (int i=n;i>=0;i--) a[i]=x%30,x/=30;} ll dfs1(ll x); ll dfs(int a[],int x,int y){ ll ans=0; //if ((++tim)%10000==0) printf("%d\n",tim); if (y>n){ if (!a[x]){ int t[maxn]; for (int i=1;i<=n;i++) t[i]=a[i]; sort(t+1,t+1+n),t[0]=x; return dfs1(encode(t)); } else return 0; } a[x]-=3; if (a[x]>=0&&a[y]>=0) ans+=dfs(a,x,y+1); a[x]+=3,a[y]-=3; if (a[x]>=0&&a[y]>=0) ans+=dfs(a,x,y+1); a[y]+=3,a[x]--,a[y]--; if (a[x]>=0&&a[y]>=0) ans+=dfs(a,x,y+1); a[x]++,a[y]++;return ans; } ll dfs1(ll x){ ll tmp=h.find(x); if (tmp!=-1) return tmp; int b[maxn];decode(b,x); //if (tim%10000==0)print(b); if (b[0]==n&&b[n]==0) return 1; ll sum=dfs(b,b[0]+1,b[0]+2); h.add(x,sum);return sum; } int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n),printf("%lld\n",dfs1(encode(a))%pp); return 0; } /* 10 21 12 16 8 3 4 7 20 12 13 */