纠结这个问题已经很久了,题目不难,简单的DP就行了。可是也可以算是网络流建模的经典例题。今天终于调过了NOIp2008的传纸条,顺便把程序改了改交了NOIp2000的方格取数与3取方格数、n取方格数。
传纸条的算法见NOIP2008 传纸条 费用流建模
原来以为只有输入不同,但是传纸条与方格取数还是不同的,传纸条要求每个同学只能帮一次,所以拆点后连一条边即可,而方格取数可以重复经过(虽然数字已经被拿走了),所以还要连一条费用为0,容量为INF的边,才可以在选择之后重复经过。开始时没考虑到,WA了好多次。
关于方格取数,可以参考从2取方格数到N取方格数和N取方格数,讲得都很好,就是代码恶心了点(好吧我承认我的代码也很丑)
3取方格数和N取方格数在tyvj上有,P1413和P1414。传纸条和方格取数都是NOIp的原题,到处都有。另外HDU上有方格取数的改编,有空就去把它搞掉吧。
发现不常练习真的是不行的。高考放假后到现在十几天没写费用流了,现在写个裸的费用流还花很长时间,还是抄自己原来代码的,还一堆bug……
另外写这种超过一百行的代码要规范些才好,我的代码还是很丑的,比如队列的操作可以放到过程里,等等。
附传纸条与N取方格数源程序,只是add_edge和init过程略有不同。
N取方格数:
const oo=19930317; var e,c,next,opp,q,cost:array[0..100000] of longint; dist,g,pre,pre_edge:array[0..100000] of longint; x:array[0..100,0..100] of longint; flag:array[0..100000] of boolean; i,j,k,a,b:longint; max_cost:longint; s,t:longint; n,m:longint; size:longint; now,flow,p:longint; function min(a,b:longint):longint; begin if a<b then exit(a) else exit(b); end; procedure add_edge(x,y,z,w:longint); begin inc(size); e[size]:=y; c[size]:=w; next[size]:=g[x]; g[x]:=size;cost[size]:=z;opp[size]:=size+1; inc(size); e[size]:=x; c[size]:=0; next[size]:=g[y]; g[y]:=size;cost[size]:=-z;opp[size]:=size-1; end; function spfa:boolean; var head,tail,i,j,p:longint; begin head:=0; tail:=1; q[1]:=s; for i:=1 to t do dist[i]:=-oo; dist[s]:=0; fillchar(flag,sizeof(flag),0); flag[s]:=true; while head<tail do begin inc(head); i:=q[head]; flag[i]:=false; p:=g[i]; while p<>0 do begin j:=e[p]; if (dist[i]+cost[p]>dist[j])and(c[p]>0) then begin dist[j]:=cost[p]+dist[i]; pre[j]:=i; pre_edge[j]:=p; if not flag[j] then begin inc(tail); q[tail]:=j; flag[j]:=true; end; end; p:=next[p]; end; end; exit(dist[t]<>-oo); end; procedure init; begin readln(k,m,n); j:=1; for i:=1 to n do for j:=1 to m do read(x[i,j]); for i:=1 to n do for j:=1 to m do begin a:=m*(i-1)+j; b:=a*2; a:=b-1; add_edge(a,b,x[i,j],1); add_edge(a,b,0,oo); end; for i:=1 to n do begin for j:=1 to m do begin a:=(m*(i-1)+j)*2; if j<m then add_edge(a,((i-1)*m+j+1)*2-1,0,oo); if i<n then add_edge(a,((i*m)+j)*2-1,0,oo); end; end; s:=n*m*2+1; t:=s+1; add_edge(s,1,0,oo); add_edge(n*m*2,t,0,k); end; procedure main; begin while spfa do begin now:=t; flow:=oo; while now<>s do begin flow:=min(flow,c[pre_edge[now]]); now:=pre[now]; end; now:=t; while now<>s do begin p:=pre_edge[now]; dec(c[p],flow); inc(c[opp[p]],flow); inc(max_cost,cost[p]*flow); now:=pre[now]; end; end; end; begin init; main; writeln(max_cost); end.
传纸条:
const oo=19930317; var e,c,next,opp,q,cost:array[0..100000] of longint; dist,g,pre,pre_edge:array[0..100000] of longint; flag:array[0..100000] of boolean; i,j,k,a,b,x:longint; max_cost:longint; s,t:longint; n,m:longint; size:longint; now,flow,p:longint; function min(a,b:longint):longint; begin if a<b then exit(a) else exit(b); end; procedure add_edge(x,y,z:longint); begin inc(size); e[size]:=y; c[size]:=1; next[size]:=g[x]; g[x]:=size;cost[size]:=z;opp[size]:=size+1; inc(size); e[size]:=x; c[size]:=0; next[size]:=g[y]; g[y]:=size;cost[size]:=-z;opp[size]:=size-1; end; function spfa:boolean; var head,tail,i,j,p:longint; begin head:=0; tail:=1; q[1]:=s; for i:=1 to t do dist[i]:=-oo; dist[s]:=0; fillchar(flag,sizeof(flag),0); flag[s]:=true; while head<tail do begin inc(head); i:=q[head]; flag[i]:=false; p:=g[i]; while p<>0 do begin j:=e[p]; if (dist[i]+cost[p]>dist[j])and(c[p]>0) then begin dist[j]:=cost[p]+dist[i]; pre[j]:=i; pre_edge[j]:=p; if not flag[j] then begin inc(tail); q[tail]:=j; flag[j]:=true; end; end; p:=next[p]; end; end; exit(dist[t]<>-oo); end; procedure init; begin readln(n,m); for i:=1 to n do for j:=1 to m do begin read(x); a:=m*(i-1)+j; b:=a*2; a:=b-1; add_edge(a,b,x); end; c[1]:=2; c[size-1]:=2; for i:=1 to n do begin for j:=1 to m do begin a:=(m*(i-1)+j)*2; if j<m then add_edge(a,((i-1)*m+j+1)*2-1,0); if i<n then add_edge(a,((i*m)+j)*2-1,0); end; end; s:=1; t:=n*m*2; end; procedure main; begin while spfa do begin now:=t; flow:=oo; while now<>s do begin flow:=min(flow,c[pre_edge[now]]); now:=pre[now]; end; now:=t; while now<>s do begin p:=pre_edge[now]; dec(c[p],flow); inc(c[opp[p]],flow); inc(max_cost,cost[p]*flow); now:=pre[now]; end; end; end; begin init; main; writeln(max_cost); end.