每一個節點都能放一個士兵,每一個士兵能看守與他相鄰的全部邊,求覆蓋全部邊最少須要多少士兵?ios
設 \(f[x][1/0]\) 表示回溯到第 \(x\) 個點時所用士兵數量, \(1\) 表示在這裏放一個士兵, \(0\) 表示不放優化
顯然珂推得轉移方程:spa
#include<iostream> #include<cstdio> using namespace std; const int MAXN = 1610; struct edge{ int to, nxt; }e[MAXN << 1]; int head[MAXN], num_edge; int n, k; int f[MAXN][2]; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar(); return s * w; } void add_edge(int from, int to){ e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; } void dfs(int x, int fa){ f[x][1] = 1; // cout<<x<<endl; for(int i = head[x]; i; i = e[i].nxt){ int v = e[i].to; if(v == fa) continue; dfs(v, x); f[x][1] += min(f[v][1], f[v][0]); f[x][0] += f[v][1]; } } int main() { n = read(); for(int i = 1, x; i <= n; ++i){ x = read(), x++; k = read(); for(int j = 1, v; j <= k; ++j){ v = read(), v++; add_edge(x, v), add_edge(v, x); } } dfs(1, 0); printf("%d", min(f[1][1], f[1][0])); return 0; }
例:P2458 [SDOI2006]保安站崗code
每一個保安能夠保護本身的點和相鄰點,求將樹上全部點都覆蓋最少所需保安數blog
設 \(f[u][0/1/2]\) 表示回溯到第 \(u\) 個點時所用士兵數量, \(0\) 表示在本身這裏放一個士兵, \(1\) 表示被兒子覆蓋, \(2\) 表示被父親覆蓋遊戲
轉移方程:get
感受 \(f[u][0]\) 和 \(f[u][2]\) 的轉移方程都比較顯然數學
對於 \(f[u][1]\) 由於不一樣於樹上邊覆蓋問題,它能夠被本身的兒子覆蓋,因此對兒子的要求是:要麼是被本身覆蓋,要麼被本身的兒子覆蓋string
但對於 \(u\) 自己要保證本身的兒子中有一個是被本身覆蓋,因此要求出最優的那個兒子 \(x\) 就行了,能夠枚舉全部兒子,這裏介紹一種數學式子優化io
最優的 \(x\) 知足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小
證實:
由於 \(x\) 知足 \(f[u][1] = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)
設 \(F(u, x) = f[x][0] + \sum_{v \in son[u] \And \And v != x} min(f[v][0], min[v][1])\)
假設 \(x\) 不是最優的, 則必有一個 \(y\) 知足 \(F(u, x) > F(u, y)\)
將這個式子化簡得(能夠將相同的部分消掉)
\(f[x][0] - min(f[x][0], f[x][1]) > f[y][0] - min(f[y][0], f[y][1])\)
因此有最優的 \(x\) 知足 \(f[x][0] - min(f[x][0], f[x][1])\) 最小
證畢
下面是代碼時間:
/* Work by: Suzt_ilymics Knowledge: ?? Time: O(??) */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int MAXN = 1e6+6; const int inf = 0x3f3f3f3f; struct edge{ int to, nxt; }e[MAXN << 1]; int head[MAXN], num_edge; int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0', ch = getchar(); return s * w; } int n; int f[MAXN][3]; void add_edge(int from, int to){ e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; } void dfs(int x, int fa){ int sson = 0; int minn = 988888889; for(int i = head[x]; i; i = e[i].nxt){ int v = e[i].to; if(v == fa) continue; dfs(v, x); f[x][0] += min(f[v][0], min(f[v][1], f[v][2])); f[x][2] += min(f[v][0], f[v][1]); if(f[sson][0] - min(f[sson][0], f[sson][1]) > f[v][0] - min(f[v][0], f[v][1])) sson = v; } f[x][1] = f[sson][0]; for(int i = head[x]; i; i = e[i].nxt){ int v = e[i].to; if(v == fa || v == sson) continue; f[x][1] += min(f[v][0], f[v][1]); } } int main() { n = read(); for(int i = 1, m, u, v; i <= n; ++i){ u = read(), f[u][0] = read(), m = read(); for(int j = 1; j <= m; ++j){ v = read(); add_edge(u, v), add_edge(v, u); } } f[0][0] = inf; dfs(1, 0); printf("%d", min(f[1][0], f[1][1])); return 0; }
其餘兩個樹上點覆蓋問題例題,稍微改一下輸入便可,一個套路隨便搞:
P2899 [USACO08JAN]Cell Phone Network G
最後歡迎你們來補充啊,團隊私題要是涉及隱私的話能夠聯繫我