P3230 [HNOI2013]比赛 题解

哈希+记忆化搜索。

正常搜索就对于每一次比赛有三种情况,第一组赢,平,第二组赢,然后判断最后分数是否符合题意即可。

剪枝1:无效性剪枝,若一个组枚举完后没有分数符合条件,返回 \(0\)

剪枝2:无效性剪枝,若一个组还剩的比赛全胜也无法符合分数条件,返回 \(0\)

剪枝3:优化搜索顺序,从分数大的来搜,得到赢局更多,情况会少。

剪枝4:设胜利数为 \(x\),平局数为 \(y\),分数总合为 \(sum\),队数为 \(n\)

那么:

\[\begin{aligned} &x+y=\frac{n\times(n-1)}{2}\\ &3x+2y=num \end{aligned}\]

解方程得到比赛数量。

优化:记忆化搜索。

记录当一个组所有比赛完后,其他组的还需要得到的分数值,分数值可利用 \(30\)(当然 \(28\) 也行)进制存储,由于数组过大,利用 map 进行记忆化。

注意:用 map 时不能因为值不为 \(0\) 而返回,而是搜索这个下表是否为 map 的末端,具体看代码。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#define int long long
using namespace std;
const int N=15;
const int M=35;
const int mod=1e9+7;
int n,a[N],b[N],s[N],xt,yt;
map<int,int>m;
int cmp(int fi,int se)
{
	return fi>se;
}
int dfs(int x,int y)
{
	if(x==n)return 1;
	if(y>n)
	{
		if(a[x]>0)return 0;
		int num=0;
		for(int i=x+1;i<=n;i++)b[i]=a[i];
		sort(b+x+1,b+n+1);
		for(int i=x+1;i<=n;i++)num=num*27+b[i];
		if(m.find(num)!=m.end())return m[num];
		else return m[num]=dfs(x+1,x+2);
	}
	if((n-y+1)*3<a[x])return 0;
	int sum=0;
	if(a[x]>=3&&xt)
	{
		a[x]-=3;
		xt--;
		sum+=dfs(x,y+1);
		a[x]+=3;
		xt++;
	}
	if(a[x]>=1&&a[y]>=1&&yt)
	{
		a[x]--;
		a[y]--;
		yt--;
		sum+=dfs(x,y+1);
		a[x]++;
		a[y]++;
		yt++;
	}
	if(a[y]>=3&&xt)
	{
		a[y]-=3;
		xt--;
		sum+=dfs(x,y+1);
		a[y]+=3;
		xt++;
	}
	return sum;
}
signed main()
{
	scanf("%lld",&n);
	int sum=0;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum+=a[i];
	sort(a+1,a+1+n,cmp);
	xt=sum-n*(n-1),yt=n*(n-1)/2-xt;
	int ans=dfs(1,2);
	printf("%lld",ans);
	return 0;
}
posted @ 2023-02-24 13:32  Gmt丶Fu9ture  阅读(25)  评论(0)    收藏  举报