[HNOI2013]比赛 (用Hash实现记忆化搜索)

[HNOI2013]比赛

题目描述

沫沫非常喜欢看足球赛,但因为沉迷于射箭游戏,错过了最近的一次足球联赛。此次联 赛共N支球队参加,比赛规则如下:

(1) 每两支球队之间踢一场比赛。 (2) 若平局,两支球队各得1分。

(3) 否则胜利的球队得3分,败者不得分。 尽管非常遗憾没有观赏到精彩的比赛,但沫沫通过新闻知道了每只球队的最后总得分, 然后聪明的她想计算出有多少种可能的比赛过程。

譬如有3支球队,每支球队最后均积3分,那么有两种可能的情况:

可能性1 可能性2

 球队  A  B  C  得分   球队 A  B  C  得分
 A       -  3  0  3             A     -  0  3  3
 B       0  -  3  3             B    3  -  0  3 
 C       3  0  -  3            C    0  3  -  3 

但沫沫发现当球队较多时,计算工作量将非常大,所以这个任务就交给你了。请你计算 出可能的比赛过程的数目,由于答案可能很大,你只需要输出答案对10^9+7取模的结果

输入格式:

第一行是一个正整数N,表示一共有N支球队。 接下来一行N个非负整数,依次表示各队的最后总得分。 输入保证20%的数据满足N<=4,40%的数据满足N<=6,60%的数据满足N<=8,100%的数据 满足3<=N<=10且至少存在一组解。

输出格式:

仅包含一个整数,表示答案对10^9+7取模的结果

输入样例#1:

4
4 3 6 4

输出样例#1:

3

说明:

20%的数据满足N≤4;

40%的数据满足N≤6;

60%的数据满足N≤8;

100%的数据满足3≤N≤10且至少存在一组解。

solution:

因为数据范围小,且每一队分数不会大于27所以可以开个long long的map来进行记忆化。从第一队枚举,每枚举一对即可进行一次hash记忆化。注意一定要以每一队还需要的分数来hash不然会出错(性质不一样)(这样连样例都难过),还有hash之前排个序可以去重(快一些)(可以画图证明,枚举完一队后其他队所需分数可以互换)!

但因为是long long 的map速度不够优秀,所以剪枝很关键(暴力搜索,剪枝优秀的话可以60分)!

code:

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int
#define mod 1000000007

using namespace std;

int n,x,y,z;
int a[11],b[11],s[11];

map<ll,ll> h;

inline int qr(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch^48);
    return res;
}

inline ll dfs(int i,int j){ll res=0;
    if(a[i]-3*(n-j+1)>0)return 0;
    if(j>n){
        if(i==n-1)return 1;
        for(rg k=i+1;k<=n;++k)
            b[k]=a[k];
        sort(b+i+1,b+n+1);
        for(rg k=i+1;k<=n;++k)
            res=res*27+b[k]+1;
        if(h.find(res)!=h.end()){
            return h[res];
        }
        return h[res]=dfs(i+1,i+2);
    }
    if(a[i]>2&&x){
        a[i]-=3;--x;
        res+=dfs(i,j+1);
        a[i]+=3;++x;
    }
    if(a[j]>2&&x){
        a[j]-=3;--x;
        res+=dfs(i,j+1);
        a[j]+=3;++x;
    }
    if(a[i]&&a[j]&&y){
        --a[i];--a[j];--y;
        res+=dfs(i,j+1);
        ++a[i];++a[j];++y;
    }return res;
}

int main(){
    //freopen("match.in","r",stdin);
    //freopen("match.out","w",stdout);
    n=qr();
    for(rg i=1;i<=n;++i)
        z+=(s[i]=qr());
    sort(s+1,s+n+1);
    for(rg i=1;i<=n;++i)
        a[i]=s[i];
    x=z-n*n+n,y=(z-3*x)>>1;
    printf("%lld
",dfs(1,2)%mod);
    return 0;
}
✐☎博主撰文不易,转载还请注明出处;若对本文有疑,请私信或在下方讨论中提出。O(∩_∩)O谢谢!☏

☃〔尽管小伙伴们肯定有千百种方式针对,但博主还是极其非常十分不要脸的把反对键吃掉辣!〕☃

✿『$At$ $last$:非常一(hu)本(shuo)正(ba)经(dao)的:博主很笨,请不要欺负他』✿✍

原文地址:https://www.cnblogs.com/812-xiao-wen/p/10306353.html