洛谷1041 傳染病控制
阿新 • • 發佈:2018-12-27
原題連結
爆搜題。
有個很顯然的貪心,就是第\(i\)輪擴充套件肯定是刪去\(i\)到\(i + 1\)層上的某一條邊。
另外,貪心地刪除含子節點最多的點是錯誤的,比如一條很長的鏈和一個比鏈節點少一點但是全部分佈在一層,這樣就是錯誤的。
所以我們爆搜的列舉刪去這一層的哪個點,並累加上以該點為根的子樹大小,然後繼續搜下一層,同時將上一層被刪去的點擴充套件到這一層,即上一層被刪去的點所在子樹都不會被感染,按層標記下來。
每次搜尋的結果就是\(n\)減去所有被刪去的點,答案對每次搜尋結果取\(\min\)即可。
#include<cstdio> using namespace std; const int N = 310; const int M = 620; int fi[N], di[M], ne[M], de[N][N], L[N], fa[N], si[N], l, mi = 1e9; bool v[N]; inline int re() { int x = 0; char c = getchar(); bool p = 0; for (; c < '0' || c > '9'; c = getchar()) p |= c == '-'; for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0'; return p ? -x : x; } inline int minn(int x, int y) { return x < y ? x : y; } inline void add(int x, int y) { di[++l] = y; ne[l] = fi[x]; fi[x] = l; di[++l] = x; ne[l] = fi[y]; fi[y] = l; } void dfs_1(int x, int f, int d)//初始化子樹大小、該層的節點和每個點的父親。 { int i, y; si[x] = 1; de[d][++L[d]] = x; fa[x] = f; for (i = fi[x]; i; i = ne[i]) if ((y = di[i]) ^ f) { dfs_1(y, x, d + 1); si[x] += si[y]; } } void dfs_2(int nw, int s) { mi = minn(mi, s); if (!L[nw]) return; int i, x, k = 0; for (i = 1; i <= L[nw]; i++)//擴充套件被刪去的點 if (v[fa[x = de[nw][i]]]) v[x] = 1, k++; if (!(k ^ L[nw]))//如果這一層均被刪去,那麼就不用往下搜了 return; for (i = 1; i <= L[nw]; i++)//列舉刪點 { x = de[nw][i]; if (v[x]) continue; v[x] = 1; dfs_2(nw + 1, s - si[x]); v[x] = 0; } for (i = 1; i <= L[nw]; i++)//回溯 if (v[fa[x = de[nw][i]]]) v[x] = 0; } int main() { int i, x, y, n, m; n = re(); m = re(); for (i = 1; i <= m; i++) { x = re(); y = re(); add(x, y); } dfs_1(1, 0, 1); dfs_2(2, n); printf("%d", mi); return 0; }