noip模擬賽 罪犯分組
阿新 • • 發佈:2017-09-23
printf inf algorithm ret mil return for 發現 noi
分析:看了題後沒別的思路,感覺就是dp,普通dp的話狀態和方程實在是不好設計,觀察數據,發現N非常小,暗示了這道題要用狀壓dp來做.
先枚舉每個集合,再用O(n^2)的暴力看這個集合內有多少個沖突,如果沖突數量不大於k,那麽就可以分成1個集合了,否則一定要分成多個集合,那麽枚舉它的子集j,狀態轉移方程就出來了:f[i] = min{f[j] + f[i ^ j]} j是i的子集.
以後沒思路要多往dp上面去想,還要註意看數據範圍,有一個值特別小就很有可能是狀壓dp.
#include <cstdio> #include <cstring> #include<iostream> #include <algorithm> using namespace std; const int inf = 0x7fffffff; int n, m, k,a[20][20],f[10000010]; int main() { scanf("%d%d%d", &n, &m, &k); for (int i = 1; i < (1 << n); i++) f[i] = inf; for (int i = 1; i <= m; i++) { intx, y; scanf("%d%d", &x, &y); a[x][y] = a[y][x] = 1; } for (int i = 1; i < (1 << n); i++) { int cnt = 0; for (int j = 1; j <= n; j++) for (int l = j + 1; l <= n; l++) if (a[j][l] && (i & (1 << (j - 1)) && (i & (1 << (l - 1))))) cnt++; if (cnt <= k) f[i] = 1; else { for (int j = i; j; j = (j - 1) & i) f[i] = min(f[i], f[j] + f[i ^ j]); } } printf("%d\n", f[(1 << n) - 1]); return 0; }
noip模擬賽 罪犯分組