您的位置:首页 > 其它

[CQOI2009] 循环赛 - 暴力出奇迹

2017-01-18 21:09 134 查看

题目描述

  n支队伍打比赛,每两支队伍恰好比赛一场。平局时各得1分,而有胜负时胜者3分,负者0分。

  假设三支队伍得分分别为3, 3, 3,则可能有两种情况:

  


  给出n支队伍的最终得分(即所有比赛均已结束),统计有多少种可能的分数表。

输入格式

第一行包含一个正整数n,队伍的个数。第二行包含n个非负整数,即每支队伍的得分。

输出格式

输出仅一行,即可能的分数表数目。保证至少存在一个可能的分数表。

样例数据

样例输入

样例数据#1

3

3 3 3

样例数据#2

2

0 3

样例数据#3

3

4 1 2

样例数据#4

6

5 6 7 7 8 8

样例输出

样例数据#1

2

样例数据#2

1

样例数据#3

1

样例数据#4

121

数据规模



题目分析

麻烦的搜索题,加了无数个剪枝和优化终于过了。。。

不过比起斗地主、Mayan游戏、素数方阵这些厉害的搜索题来说还算轻松

数据很良心,纯粹爆搜可以得88分左右

剪枝1:

可行性剪枝,如果当前队伍剩下的全赢也不能达到目标或当前队伍已经超过了目标,剪。

优化2:

由于棋盘是类对称的,故只需要枚举一半的棋盘

优化3:

因为平局总分数+2,非平局总分数+3,故可以列方程求出平局与非平局的数量,作为资源分配到搜索中

剪枝4:

不必搜索出情况后检查可行性,直接计算出每一个队伍最后一场比赛的得分

优化5:

大幅度提高时间效率:Hash判重。将剩余状态的方案用Hash保存,下一次直接调用。(有点碰运气的成分)

源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
typedef long long LL;
int n,Remain[15],vst[9][174109],Hash[9][174109];
LL Dfs(int x,int y,LL Win,LL Equal) {
if(y==1) { //Hash判重
int tmp[15]= {0},num=0;
for(int i=1; i<=n-(x-1); i++)tmp[i]=Remain[(x-1)+i];
sort(tmp+1,tmp+n-(x-1)+1);
for(int i=1; i<=n-(x-1); i++)num=(num*23+tmp[i])%174107;
if(!vst[n-(x-1)][num]) {
Hash[n-(x-1)][num]=Dfs(x,x+1,Win,Equal);
vst[n-(x-1)][num]=1;
}
return Hash[n-(x-1)][num];
}
if(x>=y)return Dfs(x,x+1,Win,Equal); //只枚举一半棋盘
if(x==n&&y==n+1)return 1; //找到一个可能性
if((n-y+1)*3<Remain[x]||Remain[x]<0)return 0; //可行性剪枝
int Nextx=x,Nexty=y;
LL sum=0;
if(Nexty==n) { //最后一个位置 :计算得出
if(Remain[x]==1&&Equal>=1) {
Remain[y]--;
sum+=Dfs(x+1,1,Win,Equal-1);
Remain[y]++;
} else if(Remain[x]==0&&a
4000
mp;Win>=1) {
Remain[y]-=3;
sum+=Dfs(x+1,1,Win-1,Equal);
Remain[y]+=3;
} else if(Remain[x]==3&&Win>=1)sum+=Dfs(x+1,1,Win-1,Equal);
return sum;
} else Nexty++;
if(Equal>=1&&Remain[x]>=1&&Remain[y]>=1) {
Remain[x]--;
Remain[y]--;
sum+=Dfs(Nextx,Nexty,Win,Equal-1);
Remain[x]++;
Remain[y]++;
}
if(Win>=1) {
if(Remain[x]>=3) {
Remain[x]-=3;
sum+=Dfs(Nextx,Nexty,Win-1,Equal);
Remain[x]+=3;
}
if(Remain[y]>=3) {
Remain[y]-=3;
sum+=Dfs(Nextx,Nexty,Win-1,Equal);
Remain[y]+=3;
}
}
return sum;
}
LL sum=0,Win,Equal;
int main() {
n=Get_Int();
for(int i=1; i<=n; i++) {
Remain[i]=Get_Int();
sum+=Remain[i];
}
Win=abs(sum-(n*(n-1))); //赢或输的总次数
Equal=abs((n*(n-1)/2)-Win); //平局总次数
printf("%lld\n",Dfs(1,2,Win,Equal));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  oi 信息学 CQOI 搜索 暴力