CF-GYM 100837F Controlled Tournament
題目大意
有個人參加比賽,其中你是比賽的組織者,你必須幫助第個人使他獲勝。求讓比賽輪數最小時的方案數。
分析
看到了這麼小的,我們很容易往狀壓 DP 上想。
考慮狀態表示當前參加的人爲,我們想讓第個人勝出且經過層的方案數。
轉移就枚舉的非空子集轉移即可。
但這樣用循環直接做的話複雜度爲的。
我們發現這樣定義無用狀態其實非常多的,所以我們就用記憶化搜索來實現這個 DP 以規避無用狀態。
參考代碼
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 16;
inline int lowbit(int x) {return x & (-x);}
inline int bitcount(int x) {
int ret = 0;
while(x) ret++, x -= lowbit(x);
return ret;
}
inline int height(int x) {
int ret = 0;
while((1 << ret) < x) ret++;
return ret;
}
int N, M;
int R[Maxn + 5][Maxn + 5];
int h[Maxn + 5];
int f[7][Maxn + 3][(1 << Maxn) + 3];
int DFS(int dep, int win, int s) {
if(f[dep][win][s] != -1) return f[dep][win][s];
if(bitcount(s) == 1) {
if(s & (1 << (win - 1))) return f[dep][win][s] = 1;
else return f[dep][win][s] = 0;
}
f[dep][win][s] = 0;
for(int t = (s - 1) & s; t > 0; t = (t - 1) & s) {
int s1 = t, s2 = s ^ t;
if(!(s1 & (1 << (win - 1)))) continue;
if(h[bitcount(s1)] > dep - 1 || h[bitcount(s2)] > dep - 1)
continue;
int ans1 = DFS(dep - 1, win, s1);
int ans2 = 0;
for(int i = 1; i <= N; i++)
if((s2 & (1 << (i - 1))) && R[win][i])
ans2 += DFS(dep - 1, i, s2);
f[dep][win][s] += ans1 * ans2;
}
return f[dep][win][s];
}
int main() {
//#ifdef LOACL
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
//#endif
freopen("f.in", "r", stdin);
freopen("f.out", "w", stdout);
scanf("%d %d", &N, &M);
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
scanf("%d", &R[i][j]);
for(int i = 1; i <= N; i++)
h[i] = height(i);
memset(f, -1, sizeof f);
printf("%d\n", DFS(h[N], M, (1 << N) - 1));
return 0;
}