bzoj2324 [ZJOI2011]营救皮卡丘

上下界最小费用可行流,有tm重边,真傻逼

就是$$i->i':(1,inf,0)$$

$$i'->j:(0,inf,w)$$

$$S->0:(0,num,0)$$

$$i'->T:(0,inf,0)$$

然后智障的建图就好了。

之后顺便看了看达哥的floyed总结,深刻理解了floyed。

每一层k的循环就是更新i->j路径上经过不大于k的点的最短路(i,j可以大于k,但路径上其他点不行)。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<queue>
 7 #define N 350
 8 #define inf 0x7fffffff
 9 using namespace std;
10 int n,m,num,S,T,SS,TT;
11 int e=2,head[N];
12 struct edge{
13     int u,v,f,w,next;
14 }ed[N*N*4];
15 void add(int u,int v,int f,int w){
16     ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
17     ed[e].next=head[u];head[u]=e++;
18     ed[e].u=v;ed[e].v=u;ed[e].f=0;ed[e].w=-w;
19     ed[e].next=head[v];head[v]=e++;
20 }
21 int dis[N],from[N];
22 bool vis[N];
23 bool spfa(){
24     memset(dis,0x3f,sizeof dis);
25     memset(from,0,sizeof from);
26     memset(vis,0,sizeof vis);
27     queue<int> q;
28     q.push(S);dis[S]=0;
29     while(!q.empty()){
30         int x=q.front();q.pop();vis[x]=0;
31         for(int i=head[x];i;i=ed[i].next){
32             int v=ed[i].v;
33             if(ed[i].f&&dis[v]>dis[x]+ed[i].w){
34                 dis[v]=dis[x]+ed[i].w;from[v]=i;
35                 if(!vis[v]){vis[v]=1;q.push(v);}
36             }
37         }
38     }
39     return from[T];
40 }
41 int work(){
42     int ans=0;
43     while(spfa()){
44         int F=inf;
45         for(int i=from[T];i;i=from[ed[i].u])
46             F=min(F,ed[i].f);
47         for(int i=from[T];i;i=from[ed[i].u])
48             ans+=F*ed[i].w,ed[i].f-=F,ed[i^1].f+=F;
49     }
50     return ans;
51 }
52 int g[N][N];
53 int main(){//i->i':(1,inf,0) i'->j:(0,inf,w) S->0:(0,num,0) i'->T:(0,inf,0)
54     memset(g,0x3f,sizeof g);
55     scanf("%d%d%d",&n,&m,&num);n++;
56     S=2*n+1,T=S+1,SS=T+1,TT=SS+1;
57     for(int i=1;i<=n;i++)g[i][i]=0;
58     for(int i=1,u,v,w;i<=m;i++){
59         scanf("%d%d%d",&u,&v,&w);u++;v++;
60         g[u][v]=g[v][u]=min(g[u][v],w);
61     }
62     for(int k=1;k<=n;k++)
63         for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
64             if((k<=i||k<=j)&&g[i][j]>g[i][k]+g[k][j])
65                 g[i][j]=g[i][k]+g[k][j];
66     for(int i=1;i<=n;i++){
67         add(i,n+i,inf,0);add(n+i,T,inf,0);
68         add(i,TT,1,0);add(SS,n+i,1,0);
69         for(int j=i+1;j<=n;j++)add(n+i,j,inf,g[i][j]);
70     }
71     add(S,1,num,0);add(T,S,inf,0);
72     swap(S,SS),swap(T,TT);
73     printf("%d\n",work());
74     return 0;
75 }
View Code

 

转载于:https://www.cnblogs.com/Ren-Ivan/p/8305898.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值