您的位置:首页 > 其它

51nod 1299 监狱逃离

2017-11-29 12:30 239 查看
原题链接.

初二的时候就听老曹讲过这题。

据说一个最小割就过了。

最小割特别显然。

每个点x->x’连代价为1的边,不要这个点就是割掉这条边。

对于每个有人的点x,S->x连正无穷。

对于每个叶子节点x,x->T连正无穷。

对于每条树边x->y,x->y’连正无穷。

跑最大流=最小割,就是答案。

100000需要梦想。

正解是个辣鸡树形dp。

选一个叶子节点为根。

fi,0/1/2分别表示:

0.子树中人不能到,没有到叶子的路径。

1.子树中人不能到,有路径。

2.子树中人能到,没有路径。

强行dp,什么情况都丢进去就好了。

所以我的方程写的很丑。

Code:

#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 1e5 + 5;

int n, m, x, y, r
, bz
;
int tot, next[N * 2], to[N * 2], final
;

void link(int x, int y) {
next[++ tot] = final[x], to[tot] = y, final[x] = tot;
next[++ tot] = final[y], to[tot] = x, final[y] = tot;
}

int g, bx
, f
[3];

void dg(int x) {
bx[x] = 1;
if(r[x] == 1 && x != g) {
f[x][2] = n;
f[x][1] = 0;
f[x][0] = 1;
return;
}
if(bz[x]) f[x][2] = 0, f[x][0] = f[x][1] = n;
int m0 = 1, s = 0;
for(int i = final[x]; i; i = next[i]) {
int y = to[i]; if(bx[y]) continue;
dg(y);
if(bz[x]) {
f[x][2] += min(f[y][0], f[y][2]);
} else {
m0 += min(f[y][0], min(f[y][1], f[y][2]));
f[x][0] += f[y][0];
f[x][1] += min(f[y][0], f[y][1]);
f[x][2] += min(f[y][0], f[y][2]);
}
}
if(!bz[x]) f[x][0] = min(f[x][0], m0);
if(f[x][0] > n) f[x][0] = n;
if(f[x][1] > n) f[x][1] = n;
if(f[x][2] > n) f[x][2] = n;
}

int main() {
scanf("%d %d", &n, &m);
n ++;
fo(i, 1, n - 1) {
scanf("%d %d", &x, &y); x ++; y ++;
r[x] ++; r[y] ++;
link(x, y);
}
fo(i, 1, m) {
scanf("%d", &x); x ++;
bz[x] = 1;
if(r[x] == 1) {
printf("-1"); return 0;
}
}
g = 0;
fo(i, 1, n) if(r[i] == 1)
g = i;
dg(g);
f[g][2] ++;
printf("%d", min(f[g][0], min(f[g][1], f[g][2])));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: