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;
}

浙公网安备 33010602011771号