[HNOI2013]比赛

Description

Luogu3230
BZOJ3139
其实这道题和[CQOI2009](Luogu3154, BZOJ1306)重了。

Solution

搜索。枚举每场比赛的对手。要有两个剪枝:

  1. 可行性剪枝,就是如果每一场都赢也不能达到目标分数就剪掉。
  2. 记忆化,在要枚举下一个人的时候,可以记忆化一下:如果剩下的人的分数的情况已经算过了,就不用再算了,注意这个时候剩下的人是无序的(因为他们之间还没有比赛)。
    当然,为了方便,我们用扣分代替加分,最终分数为\(0\)即合法。

Code

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <map>

typedef unsigned long long ll;
const int N = 11;
const ll MOD = 1e9 + 7;

std::map<ll, int> f;
int a[N], b[N], n;

inline ll hash(int x) {
    ll ans = x-1;
    for (int i = 1; i < x; ++i) ans = ans * 30 + b[i];
    return ans;
}

ll dfs(int x, int y) { // x和y大战 
    if (a[x] > (x-y) * 3) return 0; // 如果一直都赢也拿不到应有的分就gg 
    if (x == y) {
        if (x == 1) return 1; // 搜到最后一个人结束 
        for (int i = 1; i < x; ++i) b[i] = a[i];
        std::sort(b+1, b+x); // mark : 这里的上界
        ll hs = hash(x);
        return f.count(hs) ? f[hs] : f[hs] = dfs(x-1, 1);
    }
    ll ans = 0;
    if (a[x] >= 3) {
        a[x] -= 3;
        ans += dfs(x, y+1);
        a[x] += 3;
    }
    if (a[x] && a[y]) {
        a[x]--; a[y]--;
        ans += dfs(x, y+1);
        a[x]++; a[y]++;
    }
    if (a[y] >= 3) {
        a[y] -= 3;
        ans += dfs(x, y+1);
        a[y] += 3;
    }
    return ans;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    std::cout << dfs(n, 1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wyxwyx/p/hnoi2013match.html