[bzoj3139] 比赛
Description
沫沫非常喜欢看足球赛,但因为沉迷于射箭游戏,错过了最近的一次足球联赛。此次联赛共\(N\)支球队参加,比赛规则如下:
(1) 每两支球队之间踢一场比赛。
(2) 若平局,两支球队各得\(1\)分。
(3) 否则胜利的球队得\(3\)分,败者不得分。
尽管非常遗憾没有观赏到精彩的比赛,但沫沫通过新闻知道了每只球队的最后总得分, 然后聪明的她想计算出有多少种可能的比赛过程。
譬如有\(3\)支球队,每支球队最后均积\(3\)分,那么有两种可能的情况:
可能性1:
球队 | \(A\) | \(B\) | \(C\) | 得分 |
---|---|---|---|---|
\(A\) | \(-\) | \(3\) | \(0\) | \(3\) |
\(B\) | \(0\) | \(-\) | \(3\) | \(3\) |
\(C\) | \(3\) | \(0\) | \(-\) | \(3\) |
可能性2:
球队 | \(A\) | \(B\) | \(C\) | 得分 |
---|---|---|---|---|
\(A\) | \(-\) | \(0\) | \(3\) | \(3\) |
\(B\) | \(3\) | \(-\) | \(0\) | \(3\) |
\(C\) | \(0\) | \(3\) | \(-\) | \(3\) |
但沫沫发现当球队较多时,计算工作量将非常大,所以这个任务就交给你了。请你计算出可能的比赛过程的数目,由于答案可能很大,你只需要输出答案对\(10^9+7\)取模的结果
Input
第一行是一个正整数\(N\),表示一共有\(N\)支球队。 接下来一行\(N\)个非负整数,依次表示各队的最后总得分。
输入保证\(20\%\)的数据满足\(N\le 4\),\(40\%\)的数据满足\(N\le 6\),\(60\%\)的数据满足\(N\le 8\),\(100\%\)的数据满足\(3\le N\le 10\)且至少存在一组解。
Output
仅包含一个整数,表示答案对\(10^9+7\)取模的结果
Sample Input
4
4 3 6 4
Sample Output
3
Source
\(Hnoi2013\)
题解
很久很久做的题,先上\(Dfs+\)减枝,简单的搜索,不必讲解。
#include<iostream>
#include<cstdio>
using namespace std;int n,a[10],now[10],Ans;
inline void Scanf(int &x)
{
x=0;char s=getchar();
while(s<'0'||s>'9')s=getchar();
while(s>='0'&&s<='9')x=x*10+s-'0',s=getchar();
}void Dfs(int t,int T){
if(now[t]>a[t]||now[T]>a[T])return;
if(now[t]+(n-T+1)*3<a[t])return;
if(now[T]+(n-t+1)*3<a[T])return;
if(t==n){++Ans;return;}
if(T==n){int YH=a[t]-now[t];
if(YH==1){++now[t],++now[T];Dfs(t+1,t+2);--now[t],--now[T];}
if(YH==3){now[t]+=3;Dfs(t+1,t+2);now[t]-=3;}
if(YH==0){now[T]+=3;Dfs(t+1,t+2);now[T]-=3;}
return;
}
++now[t],++now[T];Dfs(t,T+1);
--now[t],--now[T];
now[t]+=3;Dfs(t,T+1);now[t]-=3;
now[T]+=3;Dfs(t,T+1);now[T]-=3;
}int main(){
int i;Scanf(n);
for(i=1;i<=n;++i)Scanf(a[i]);
Dfs(1,2),printf("%d\n",Ans);
return 0;
}
正解是记忆化搜索,从目标状态往前搜索,用\(Hash\)记录每一种状态,具体见代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=15,maxn=1000007,inf=1000000007;
int n,num[N],tmp[N];
struct node
{
ll id;
int ans,next;
}Hash[maxn+1];
int hd[maxn+1],cnt;
ll GetHash(int nw)
{
for(int i=1;i<=n;++i) tmp[i]=num[i];
sort(tmp+1,tmp+n+1);
ll Res=0;
for(int i=1;i<=n;++i) Res=Res*27+tmp[i];//10个球队,最高比分为27
return Res*27+nw;//由于当状态相同时未比赛的队伍数必须相同,所以还要把nw也算入Hash值
}
int Find(ll id)//差询
{
int Id=id%maxn;
for(int i=hd[Id];i;i=Hash[i].next)
if(Hash[i].id==id) return Hash[i].ans;
return -1;
}
int Insert(ll id,int ans)//将新计算的值记录于Hash表
{
int YH=id%maxn;
++cnt,Hash[cnt]=(node){id,ans,hd[YH]},hd[YH]=cnt;
return ans;
}
int Dfs(int nw,int nt)//nw表示当前枚举的球队,nt表示与之比赛的球队
{
if(nw==n)
{
if(num[n]) return 0;
return 1;//初始值为0
}
if(nt>n)
{
if(num[nw]) return 0;
ll id=GetHash(nw);//计算Hash值
int YH=Find(id);//在Hash表查询次数
if(YH!=-1) return YH;//已计算
return Insert(id,Dfs(nw+1,nw+2));//记录
}
int Res=0;
if(num[nw]>=3)//与之比赛的球队赢
{
num[nw]-=3,
Res=(Res+Dfs(nw,nt+1))%inf,
num[nw]+=3;
}
if(num[nw]>=1&&num[nt]>=1)//平局
{
--num[nw],--num[nt],
Res=(Res+Dfs(nw,nt+1))%inf,
++num[nw],++num[nt];
}
if(num[nt]>=3)//当前枚举的球队赢
{
num[nt]-=3,
Res=(Res+Dfs(nw,nt+1))%inf,
num[nt]+=3;
}
return Res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&num[i]);
sort(num+1,num+n+1);//可以去掉
int Ans=Dfs(1,2);
printf("%d\n",Ans);
return 0;
}
本文作者:OItby @ https://www.cnblogs.com/hihocoder/
未经允许,请勿转载。