[HNOI2013]比赛
Description
Luogu3230
BZOJ3139
其实这道题和[CQOI2009](Luogu3154, BZOJ1306)重了。
Solution
搜索。枚举每场比赛的对手。要有两个剪枝:
- 可行性剪枝,就是如果每一场都赢也不能达到目标分数就剪掉。
- 记忆化,在要枚举下一个人的时候,可以记忆化一下:如果剩下的人的分数的情况已经算过了,就不用再算了,注意这个时候剩下的人是无序的(因为他们之间还没有比赛)。
当然,为了方便,我们用扣分代替加分,最终分数为\(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;
}