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
",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
",dfs1(encode(a))%pp);
	return 0;
}

/*
10
21 12 16 8 3 4 7 20 12 13
*/



原文地址:https://www.cnblogs.com/thythy/p/5493489.html