题意:一个可能有重边、自环的无向图,现在一些点损坏了。给定一些已知的点,且保证这些点没有损坏,但是这些点均不能通过没有损坏的点到达标号为1的点。试求最少的不能够通过没有损坏的点到达标号为1的点的数目(损坏的点也算)。
Sol:
一开始想的是求割点+树形dp,后来发现沙茶了。
首先发现一个性质,对于给定已知点p,若存在路径p->u,那么必然有点u损坏或点u不能通过没有损坏的点到达标号为1的点。
那么在最优意义下,如何满足这些限制呢?
一种显然可行的方法为:将与给定点相邻的点全部标记为损坏。我们容易证明其满足上述性质。
事实上,这是一种最优的选择。
怎么证明呢。我们可以想象,损坏点以给定点为中心形成了一圈轮廓,那么轮廓内的点显然全部是不可达的。那么我们只需要让轮廓尽量小就好了。那么最小的轮廓恰是上述的选择方式。
由于显然两个给定点之间的处理不相互影响,这样做是正确的。
那么标记过后flood-fill一下就行了。
#include <cstdio> #include <cstring> #include <cctype> #include <iostream> #include <algorithm> using namespace std; #define N 30010 #define M 100010 int head[N], next[M << 1], end[M << 1]; void addedge(int a, int b) { static int q = 1; end[q] = b; next[q] = head[a]; head[a] = q++; } void make(int a, int b) { addedge(a, b); addedge(b, a); } bool cut[N]; bool vis[N]; void dfs(int x) { vis[x] = 1; for(int j = head[x]; j; j = next[j]) if (!cut[end[j]] && !vis[end[j]]) dfs(end[j]); } int main() { int n, m, p; scanf("%d%d%d", &n, &m, &p); register int i, j; int a, b, x; for(i = 1; i <= m; ++i) { scanf("%d%d", &a, &b); make(a, b); } while(p--) { scanf("%d", &x); for(j = head[x]; j; j = next[j]) cut[end[j]] = 1; } dfs(1); int ans = n; for(i = 1; i <= n; ++i) if (vis[i]) --ans; printf("%d", ans); return 0; }