[bzoj3139][HNOI2013]比赛

题目大意

现有N个队伍,每两个队伍对决,胜者得3分,平局各的一分。给出每个队伍最终得分,求有多少种比赛可能。N<=10。

深搜

我们可以深搜两两队之间较量的结果,每次将一个队与后面所有队做完,若此时该队总分为0就继续深搜。假设当前做到第i队与第j队间的比赛,当 3(nj+1)<a[i] ,则一定无法满足可以不继续深搜来优化算法。

记忆化搜索

我们可以用map帮助实现记忆化搜索,用 f[i][s] 表示做到第i个队了,目前分数集合为s,用29进制表示。我们发现打乱分数顺序不会有问题,因此可以排序来剪枝。

参考程序

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const ll maxn=15,mo=1000000007;
map<ll,ll> f[maxn];
ll i,j,k,l,t,n,m,ans,top;
ll a[maxn],b[maxn];
ll dfs(ll);
ll calc(ll x,ll y,ll now){
    if (x>n){
        if (y!=0) return 0;
        else return dfs(now+1);
    }
    if (3*(n-x+1)<y) return 0;
    ll cnt=0;
    if (y>=3) cnt=(cnt+calc(x+1,y-3,now))%mo;
    if (y>=1&&a[x]>=1){
        a[x]--;
        cnt=(cnt+calc(x+1,y-1,now))%mo;
        a[x]++;
    }
    if (a[x]>=3){
        a[x]-=3;
        cnt=(cnt+calc(x+1,y,now))%mo;
        a[x]+=3;
    }
    return cnt;
}
ll dfs(ll x){
    if (x==n) return (a[n]==0);
    ll i;
    top=0;
    fo(i,x,n) b[++top]=a[i];
    sort(b+1,b+top+1);
    ll j=0;
    fo(i,1,top) j=j*29+b[i];
    if (f[x].find(j)!=f[x].end()) return f[x][j];else return f[x][j]=calc(x+1,a[x],x);
}
int main(){
    scanf("%lld",&n);
    fo(i,1,n) scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    printf("%lld\n",dfs(1));
    return 0;
}

你可能感兴趣的:([bzoj3139][HNOI2013]比赛)