bzoj 1877: [SDOI2009]晨跑

1877: [SDOI2009]晨跑

Time Limit: 4 Sec  Memory Limit: 64 MB
Submit: 395  Solved: 223
[Submit][Status][Discuss]

Description

Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等等,不过到目前为止,他坚持下来的只有晨跑。现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发跑到学校,保证寝室编号为1,学校编号为N Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天数尽量长。除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计一套满足他要求的晨跑计划。

Input

第一行:两个数N,M。表示十字路口数和街道数。接下来M行,每行3个数a,b,c,表示路口a和路口b之间有条长度为c的街道(单向)。

Output

两个数,第一个数为最长周期的天数,第二个数为满足最长天数的条件下最短的路程长度。

Sample Input

7 10
1 2 1
1 3 1
2 4 1
3 4

1
4 5 1
4 6 1
2 5 5
3 6 6
5 7 1
6 7 1

Sample Output

2 11

HINT

对于30%的数据,N ≤ 20M ≤ 120
对于100%的数据,N ≤ 200M ≤ 20000

 

 

这题还是比较明显的费用流的。

要求一个路口不能经过两次以上。那么就把每个点拆成两个分别放在A部和B部里面。对于每个输入的x,y,z连一条AxBy,流量为1,费用为z的边。再从每个BiAi连一条流量为1,费用为0的边控制只能走一次。源即1,汇为n+n

一开始AB两部搞反了。。。就WA了。。。

 

AC CODE

program bzoj_1877;

var line,capa,cost,next:array[0..1000000] of longint;

    en,hs,f,dis:array[0..500] of longint;

    v,tt:array[0..5000] of boolean;

    n,len,ans:longint;

//============================================================================

procedure ins(x,y,z1,z2:longint);

begin

  inc(len); line[len]:=y;

  capa[len]:=z1; cost[len]:=z2;

  next[len]:=en[x]; en[x]:=len;

end;

//============================================================================

procedure init;

var x,y,z,m,i:longint;

begin

  readln(n,m); len:=1;

  for i:=1 to m do

  begin

    readln(x,y,z);

    ins(x,y+n,1,z);

    ins(y+n,x,0,-z);

  end;

  for i:=1 to n do

  begin

    ins(i,n+i,0,0);

    ins(n+i,i,1,0);

  end; n:=n+n;

end;

//============================================================================

function dfs(s,flow:longint):longint;

var i,rest,fix,t:longint;

begin

  if s=n then

  begin

    inc(ans,dis[n]*flow);

    exit(flow);

  end; i:=hs[s]; dfs:=0;

  v[s]:=false; tt[s]:=false;

  while i<>0 do

  begin t:=line[i];

    rest:=flow-dfs;

    fix:=dis[s]+cost[i]-dis[t];

    if rest>capa[i] then rest:=capa[i];

    if rest>0 then if fix<f[t] then f[t]:=fix;

    if (rest>0) and (fix=0) and tt[t] then

    begin

      rest:=dfs(t,rest); inc(dfs,rest);

      dec(capa[i],rest); inc(capa[i xor 1],rest);

    end;

    if dfs=flow then break;

    i:=next[i]; hs[s]:=i;

  end; tt[s]:=true;

end;

//============================================================================

procedure zkw;

var i,tmp,day:longint;

begin

  fillchar(dis,sizeof(dis),0);

  fillchar(tt,sizeof(tt),true);

  day:=0; ans:=0;

  while true do

  begin

    for i:=1 to n do hs[i]:=en[i];

    for i:=1 to n do v[i]:=true;

    for i:=1 to n do f[i]:=maxlongint;

    inc(day,dfs(1,maxlongint));

    tmp:=maxlongint;

    for i:=1 to n do

      if v[i] and (f[i]<tmp) then tmp:=f[i];

    if tmp=maxlongint then break;

    for i:=1 to n do

      if v[i] then inc(dis[i],tmp);

  end; writeln(day,' ',ans);

end;

//============================================================================

begin

  init;

  zkw;

end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值