题解 [HNOI2013]比赛
一道不错的搜索题
题面很简单,n支队伍循环赛,只知道最后各个队的得分,求可能的比赛情况数量
看数据范围只有10,上搜索
1.裸暴力
应该很好打,直接枚举每一场比赛的得分情况,然后check
复杂度\(O(3^{n*(n-1)/2})\),可以过40pts
dfs中的x,y表示谁和谁比赛
score是枚举比赛情况所推出的分数
void dfs(int x,int y)
{
if(x==n-1&&y==n+1)
{
if(check())ans++;
return;
}
if(y>n){dfs(x+1,x+2);return;}
++score[x],++score[y];
if(score[x]<=a[x]&&score[y]<=a[y])
{++nowt,dfs(x,y+1),--nowt;}
--score[x],--score[y];
score[x]+=3;if(score[x]<=a[x]){++noww,dfs(x,y+1),--noww;}score[x]-=3;
score[y]+=3;if(score[y]<=a[y]){++noww,dfs(x,y+1),--noww;}score[y]-=3;
}
2.剪枝
(1)如果当前的分数(score)已经超过了结果分数(a),因为不可能扣分,所以肯定不能满足条件(在以上代码有体现)
(2)如果某一队在剩下的所有场次中获胜都不能达到结果分数,那么也不能满足条件
if(score[x]+3*(n-y+1)<a[x])return ;
(3)限制获胜和平局的场数.这个可以通过所有队伍的分数总和以及队伍数求出
win=sum-n*(n-1),tie=n*(n-1)/2-win;
这样可以得60pts
3.记忆化
这个题目最关键的步骤
其实最后的结果数只和每个队伍的分数以及队伍数有关
在搜索一个队伍的状态下,到下一层时,可以
那么可以用记忆化,
通过hash存储对应情况下的得分情况
记得排序以排除冗余状态
AC代码:
#include<bits/stdc++.h>
#define il inline
#define R register int
#define ll long long
#define gc getchar
#define mod 1000000007
using namespace std;
il int rd()
{
R x=0,flag=1;
char ch=0;
while((ch>'9'||ch<'0')&&ch!='-')ch=gc();
if(ch=='-')flag=-1,ch=gc();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=gc();
return x*flag;
}
map<ll,ll> mp;
const int P=28;
int a[20],score[20],res[20],n,ans,sum;
int tie,win,nowt,noww;
inline bool check()
{
int i=1;
while(score[i]==a[i]&&i<=n)i++;
if(i==n+1)return true;
else return false;
}
ll dfs(int x,int y)
{
if(x==n)
return 1;
if(score[x]+3*(n-y+1)<a[x])return 0;//不加的话会错
if(y>n)
{
ll state=0;
for(R i=x+1;i<=n;i++)
res[i]=a[i]-score[i];
sort(res+x+1,res+n+1);
for(R i=x+1;i<=n;i++)
state=state*P+res[i];//因为要排序,所以分开写
if(mp.find(state)!=mp.end())return mp[state];
mp[state]=dfs(x+1,x+2);
return mp[state];
}
ll cnt=0;
++score[x],++score[y];
if(score[x]<=a[x]&&score[y]<=a[y]&&nowt<tie)
{++nowt,cnt+=dfs(x,y+1),--nowt;}
--score[x],--score[y];
score[x]+=3;
if(score[x]<=a[x]&&noww<win){++noww,cnt+=dfs(x,y+1),--noww;}
score[x]-=3;
score[y]+=3;
if(score[y]<=a[y]&&noww<win){++noww,cnt+=dfs(x,y+1),--noww;}
score[y]-=3;
return cnt;
}
int main ()
{
// freopen("match.in","r",stdin);
// freopen("match.out","w",stdout);
n=rd();
for(R i=1;i<=n;i++)a[i]=rd(),sum+=a[i];
win=sum-n*(n-1),tie=n*(n-1)/2-win;
sort(a+1,a+n+1);
cout<<dfs(1,2)%mod<<endl;
return 0;
}