bzoj2055: 80人环游世界(可行流)

传送门

表示完全看不懂最小费用可行流……

据某大佬说

我们考虑拆点,然后进行如下连边

$s$向$a_i$连边,权值$0$,容量$[0,m]$

$a_i$向$a_i‘$连边,权值$0$容量$[v_i,v_i]$

如果存在边$(i,j)$,则连边$a_i‘->a_i$,权值为$w_{i,j}$,容量$[0,m]$

$a_i‘$向$t$连边,权值$0$,容量$[0,m]$

$t$向$t‘$连边,权值$0$容量$[0,m]$

然后跑个最小费用可行流

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #define inf 0x3f3f3f3f
 7 using namespace std;
 8 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 9 char buf[1<<21],*p1=buf,*p2=buf;
10 inline int read(){
11     #define num ch-‘0‘
12     char ch;bool flag=0;int res;
13     while(!isdigit(ch=getc()))
14     (ch==‘-‘)&&(flag=true);
15     for(res=num;isdigit(ch=getc());res=res*10+num);
16     (flag)&&(res=-res);
17     #undef num
18     return res;
19 }
20 const int N=505,M=500005;
21 int head[N],Next[M],ver[M],edge[M],flow[M],tot=1;
22 inline void add(int u,int v,int e,int f){
23     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e,flow[tot]=f;
24     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=-e,flow[tot]=0;
25 }
26 int dis[N],vis[N],cur[N],S,T,ans,s,t,kt,n,m;
27 queue<int> q;
28 bool spfa(){
29     memset(dis,-1,sizeof(dis));
30     memset(vis,0,sizeof(vis));
31     memcpy(cur,head,sizeof(head));
32     q.push(T),dis[T]=0,vis[T]=1;
33     while(!q.empty()){
34         int u=q.front();q.pop();vis[u]=0;
35         for(int i=head[u];i;i=Next[i])
36         if(flow[i^1]){
37             int v=ver[i],e=edge[i];
38             if(dis[v]<0||dis[v]>dis[u]-e){
39                 dis[v]=dis[u]-e;
40                 if(!vis[v]) vis[v]=1,q.push(v);
41             }
42         }
43     }
44     return ~dis[S];
45 }
46 int dfs(int u,int limit){
47     if(!limit||u==T) return limit;
48     int fl=0,f;vis[u]=1;
49     for(int i=cur[u];i;cur[u]=i=Next[i]){
50         int v=ver[i];
51         if(dis[v]==dis[u]-edge[i]&&!vis[v]&&(f=dfs(v,min(limit,flow[i])))){
52             fl+=f,limit-=f;
53             ans+=f*edge[i];
54             flow[i]-=f,flow[i^1]+=f;
55             if(!limit) break;
56         }
57     }
58     vis[u]=0;
59     return fl;
60 }
61 void zkw(){
62     while(spfa()) dfs(S,inf);
63 }
64 int main(){
65     //freopen("testdata.in","r",stdin);
66     n=read(),m=read(),s=2*n+1,t=s+1,kt=t+1,S=kt+1,T=S+1;
67     for(int i=1;i<=n;++i){
68         int x=read();
69         add(S,i+n,0,x),add(i,T,0,x),
70         add(s,i,0,m),add(i+n,t,0,m);
71     }
72     add(t,kt,0,m),add(kt,s,0,inf);
73     for(int i=1;i<=n;++i)
74     for(int j=i+1;j<=n;++j){
75         int x=read();
76         if(~x) add(n+i,j,x,inf);
77     }
78     zkw();
79     printf("%d\n",ans);
80     return 0;
81 }

原文地址:https://www.cnblogs.com/bztMinamoto/p/9580061.html

时间: 2024-10-09 12:24:41

bzoj2055: 80人环游世界(可行流)的相关文章

bzoj千题计划159:bzoj2055: 80人环游世界(上下界费用流)

http://www.lydsy.com/JudgeOnline/problem.php?id=2055 某个国家必须经过vi次, 可以转化为上下界都为vi的边 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 205 #define M 10500 const int inf=1e9; int sr

BZOJ2055 80人环游世界

m个人随便设定起点= =,所以是有上下界网络流喽... 今天刚学,于是试着写了写,1A耶! 费用流是板子哦~ 1 /************************************************************** 2 Problem: 2055 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:536 ms 7 Memory:8624 kb 8 ******************************

[bzoj2055]80人环游世界[网络流,上下界网络流]

手动画了整张图,,算是搞懂了吧,, 1 #include <bits/stdc++.h> 2 3 #define INF 0x3f3f3f3f 4 5 using namespace std; 6 7 template<const int _n,const int _m> 8 struct Edge 9 { 10 struct Edge_base { int to,next,w,c; }e[_m]; int cnt,p[_n]; 11 Edge() { clear(); } 12

【BZOJ-2055】80人环游世界 上下界费用流 (无源无汇最小费用最大流)

2055: 80人环游世界 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 321  Solved: 201[Submit][Status][Discuss] Description 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么 一个80人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家. 因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为

bzoj 2055: 80人环游世界 -- 上下界网络流

2055: 80人环游世界 Time Limit: 10 Sec  Memory Limit: 64 MB Description 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么 一个80人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家. 因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为1...N.假若第i个人的游历路线为P1.P2......Pk(0≤k≤N),则P1<P2&

【bzoj2055】80人环游世界 有上下界费用流

原文地址:http://www.cnblogs.com/GXZlegend 题目描述 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么 一个80人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家. 因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为1...N.假若第i个人的游历路线为P1.P2......Pk(0≤k≤N),则P1<P2<......<Pk. 众所周知,中

bzoj 2055 80人环游世界

有源汇上下界最小费用可行流. 将每个国家拆点. 源点向一个新建节点连一条上界为总人数下界为0费用为0的边. 新建节点向每个国家的入点连一条上界为正无穷下界为0费用为0的边. 每个国家的入点向出点连一条上下界均为该国家访问人数费用为0的边. 每个国家的出点向汇点连一条上界为正无穷下界为0费用为0的边. 对于国家i能到国家j,i的出点向j的入点连一条上界为正无穷下界为0费用为机票费的边. 然后对图求一边有源汇上下界最小费用可行流即可得出最小费用. 具体做法是先将所有边的下界乘费用相加,这一部分是必须

P4553 80人环游世界 上下界费用流

题意: 有1-n个城市  有m个人去旅游  每个人只能从编号小的城市到编号大的城市  花费费用为COSTij   一开始每个人可以从任意一个城市开始旅行   且每个城市恰好有Vi个人经过(一开始也算经过) 问最小花费 显然用有上下界的费用流非常好理解  每个点进行拆点 两点之间连上界和下界都为Vi 表示正好通过Vi人      然后经典连图即可 再控制一下人数m  跑模板 (正面建图) #include<bits/stdc++.h> using namespace std; //input b

BZOJ 2055 80人环游世界 有上下界的费用流

题目大意:给定n个点,每个点有固定的经过次数,m个人从任意节点出发任意节点结束,只能向右走,要求总边权和最小 有源汇.有上下界的费用流 其实上下界费用流有两种写法- - 一种是按照上下界网络流那么转化- - 一种是把必经边的费用减掉一个INF 跑完再加回去 我比较倾向于第一种写法- - 第二种写法在INF的取值上有点麻烦- - #include <cstdio> #include <cstring> #include <iostream> #include <al