Description
你在玩电子游戏的时候遇到了麻烦。。。。。。 你玩的游戏是在一个虚拟的城市里进行,这个城市里有n个点,都从0~n-1编了号,每两个点之间有且仅有一条路径。现在,你的敌人到这个城市来踩点了!!!为了阻止他们更好的踩点, 你决定切断他们所有踩点人员的联系,使他们孤军作战,然后在各个击破。但是这就要切断某些街道,而你每切断一条路,市民就会产生相对的不满值,不满值越大,城市的和谐度就越小。所以你现在需要知道为了使踩点人员所在的点两两之间不联通所切断的边产生的最小不满值是多少?
额,使树上m个点不联通的最小代价?就这么理解罢。
Idea
着实吓人,但事实上也不怎么可怕。
不多讨论部分分了,直接考虑100%的数据
有两种不错的方法。
Ⅰ树形DP
设f[x]为以x为根结点的子树无敌人向祖先连接最优解
设g[x]为最多只有一个敌人向祖先连接最优解。
由于这个方法没有成功融会贯通,所以只好贴别人的。
不过我并不推荐树形DP。
确乎有些小题大做
不会Ⅱ贪心(+并查集)?
没有关系,我们还有法Ⅲ
Ⅲ贪心+LCA
这是由Couzit黄靖元提供的方法。
显然,这是一棵树,所以任意两点的路径是唯一的。
我们考虑
任意两个点x,y
为使
x,y不联通
,我们最少需要删去路径上一条边。
若不考虑其他的点,我们必定是选择边权最小的边删去,显然的局部最优。
稍加思索,我们发现对于整个图(全局),这也是最优的!
因此,我们只需枚举相连通的两点,删去路径上最短边即可。
可以用
O(n2)
解决。
很蹊跷,不是吗?
有没有可能
对于多个点,只需删除一条较长的边即可呢?
我们可以尝试举一个反例。
红色点为踩点人员所在点
如果我们删去权值为3的边,是不是更优呢?然而并不是的。
不要为你的双眼所蒙蔽,如果我们删去权值为3的边,左子树的两点是联通的!
再意会意会,这种贪心策略的正确性是保证的。
最重要的,这种方式码量极少!
Code
法Ⅰ
const maxn=500;
var i,n,x,y,l,tot:longint;
yy,next,cost,g,fa,f,gu:array[1..maxn] of longint;
t:array[1..maxn] of boolean;
function max(x,y:longint):longint; begin if x>y then exit(x);exit(y);end;
function min(x,y:longint):longint; begin if x>y then exit(y);exit(x);end;
procedure make(x,y,l:longint);
begin
inc(tot);
yy[tot]:=y;
next[tot]:=gu[x];
cost[tot]:=l;
gu[x]:=tot;
end;
procedure dfs(x:longint);
var i,j,sum,y:longint;
begin
i:=gu[x];
sum:=0;
while i<>0 do begin
y:=yy[i];
if fa[x]<>y then begin
fa[y]:=x;
dfs(y);
if t[y] then begin
f[x]:=f[x]+f[y]+cost[i];
g[x]:=g[x]+f[y]+cost[i];
sum:=max(sum,cost[i]);
end else begin
f[x]:=f[x]+min(g[y]+cost[i],f[y]);
if g[y]+cost[i]>f[y] then begin
sum:=max(sum,f[y]-g[y]);
g[x]:=g[x]+f[y];
end else begin
sum:=max(sum,cost[i]);
g[x]:=g[x]+g[y]+cost[i];
end;
end;
end;
i:=next[i];
end;
if t[x] then g[x]:=f[x] else g[x]:=g[x]-sum;
end;
begin
readln(n);
for i:=1 to n-1 do begin
readln(x,y,l);
make(x+1,y+1,l);
make(y+1,x+1,l);
end;
while not eof do begin
readln(x);
t[x+1]:=true;
end;
dfs(1);
writeln(min(f[1],g[1]));
end.
法Ⅱ
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 55;
int n,ans;
int h[N],flag[N];
struct node
{
int x,y,z;
} a[N];
bool cmp(node A,node B)
{return A.z>B.z;}
int gf(int x)
{
if (x!=h[x]) h[x] = gf(h[x]);
return h[x];
}
bool link(int x,int y)
{
int i = gf(x),j = gf(y);
if (flag[i] && flag[j]) return 0;
else
{
h[i] = j;
flag[j] |= flag[i];
}
return 1;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i ++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
int x;
while (scanf("%d",&x)!=EOF) flag[x] = 1;
for (int i=1;i<=n;i ++) h[i] = i;
sort(a+1,a+n,cmp);
for (int i=1;i<n;i ++)
if (!link(a[i].x,a[i].y))
ans += a[i].z;
printf("%d\n",ans);
return 0;
}
法Ⅲ
const
maxn=50;
maxm=50;
var
n,tot:longint;
i,j,l:longint;
fx,fy:longint;
ans,min:longint;
numx,numy:longint;
bz:array[0..maxn] of boolean;
fa,q,dep:array[0..maxn] of longint;
a,map:array[0..maxn,0..maxn] of longint;
procedure dfs(x,deep:longint);
var
i:longint;
begin
if(bz[x])then exit;
bz[x]:=true;dep[x]:=deep;
for i:=1 to a[x][0] do
if(not bz[a[x][i]])then
begin
fa[a[x][i]]:=x;
dfs(a[x][i],deep+1);
end;
end;
procedure init;
var
i,j:longint;
x,y,c:longint;
begin
fillchar(map,sizeof(map),$7f);
readln(n);
for i:=1 to n-1 do
begin
readln(x,y,c);
inc(a[x][0]);a[x][a[x][0]]:=y;
inc(a[y][0]);a[y][a[y][0]]:=x;
map[x][y]:=c;map[y][x]:=c;
end;
fa[0]:=0;
while(not seekeof)do
begin
inc(tot);
readln(q[tot]);
end;
end;
procedure swap(var a,b:longint);
var
c:longint;
begin
c:=a;a:=b;b:=c;
end;
begin
init();
dfs(0,1);
for i:=1 to tot-1 do
begin
for j:=i+1 to tot do
begin
min:=maxlongint;
fx:=q[i];fy:=q[j];
if(dep[fx]<dep[fy])then swap(fx,fy);
while(dep[fx]>dep[fy])do
begin
if(map[fx,fa[fx]]<min)then
begin
min:=map[fx,fa[fx]];
numx:=fx;
numy:=fa[fx];
end;
fx:=fa[fx];
end;
while(fx<>fy)do
begin
if(map[fx,fa[fx]]<min)then
begin
min:=map[fx,fa[fx]];
numx:=fx;
numy:=fa[fx];
end;
if(map[fy,fa[fy]]<min)then
begin
min:=map[fy,fa[fy]];
numx:=fy;
numy:=fa[fy];
end;
fx:=fa[fx];fy:=fa[fy];
end;
inc(ans,min);
map[numx,numy]:=0;map[numy,numx]:=0;
end;
end;
writeln(ans);
close(input);close(output);
end.