P3230 [HNOI2013]比赛 题解

哈希+记忆化搜索。

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

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

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

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

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

那么:

x+y=n×(n1)23x+2y=num

解方程得到比赛数量。

优化:记忆化搜索。

记录当一个组所有比赛完后,其他组的还需要得到的分数值,分数值可利用 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 @   Gmt丶Fu9ture  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示