【搜索】【poj 2531】Network Saboteur
问题
给你一个n个节点的图,让你将该图分成两部分,使得两部分之间的边的权值和最大。
分析
一开始看以为是图论,但是算法比较高级。再看数据范围只有20,所以只需根据题意枚举一侧的节点即可。用dfs,其实就是生成组合数。注意状态表示和更新的方法,记下当前一共找到了几个点,最后一个点的位置,和当前得到的值。每次新加入一个节点,只需操作和加入的点有关的边即可。
有两个剪枝:
1.可行性:由于是组合数,只要搜到n div 2 个数即可确定全部情况。注意这句话要放在更新最值的后面。
2.最优性:如果当前加入新点之后的值比加之前得到的值大,则继续向下搜索,注意这里不能和当前的最大值比较。(why)该搜索的实质就是找一个点集,将其他点依次尝试加入点集,得到新值,那么如果当前新节点加入之后比先前未加该点的值都要小,则可用数学方法证明含该点之后搜到的状态都要比不含该点时小。
code
program liukeke; var map:array[0..20,0..20] of longint; v:array[1..450] of boolean; n,ans,i,j:longint; procedure dfs(depth,x,now:longint); var i,temp,j:longint; begin if now>ans then ans:=now; if depth>n div 2 then exit; for i:=x to n do if not v[i] then begin temp:=now; for j:=1 to n do if (not v[j]) then inc(temp,map[i,j]) else dec(temp,map[i,j]); if temp>now then begin v[i]:=true; dfs(depth+1,i+1,temp); v[i]:=false; end; end; end; begin readln(n); for i:=1 to n do begin for j:=1 to n do read(map[i,j]); readln; end; dfs(1,1,0); writeln(ans); end.
反思
明确搜索的思路,用正确的状态表示,便于剪枝。