UVa1658 Admiral(拆点法+最小费用流)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51253

【思路】

固定流量的最小费用流。

拆点,将u拆分成u1和u2,连边(u1,u2,1,0)表示只能经过该点一次。跑流量为2的最小费用流。

【代码】

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 #include<vector>
 5 #define FOR(a,b,c) for(int a=(b);a<(c);a++)
 6 using namespace std;
 7
 8 const int maxn = 4000+10;
 9 const int INF = 1e9;
10
11 struct Edge{ int u,v,cap,flow,cost;
12 };
13
14 struct MCMF {
15     int n,m,s,t;
16     int inq[maxn],a[maxn],d[maxn],p[maxn];
17     vector<int> G[maxn];
18     vector<Edge> es;
19
20     void init(int n) {
21         this->n=n;
22         es.clear();
23         for(int i=0;i<n;i++) G[i].clear();
24     }
25     void AddEdge(int u,int v,int cap,int cost) {
26         es.push_back((Edge){u,v,cap,0,cost});
27         es.push_back((Edge){v,u,0,0,-cost});
28         m=es.size();
29         G[u].push_back(m-2);
30         G[v].push_back(m-1);
31     }
32
33     bool SPFA(int s,int t,int flowlimit,int& flow,int& cost) {
34         for(int i=0;i<n;i++) d[i]=INF;
35         memset(inq,0,sizeof(inq));
36         d[s]=0; inq[s]=1; p[s]=0; a[s]=INF;
37         queue<int> q; q.push(s);
38         while(!q.empty()) {
39             int u=q.front(); q.pop(); inq[u]=0;
40             for(int i=0;i<G[u].size();i++) {
41                 Edge& e=es[G[u][i]];
42                 int v=e.v;
43                 if(e.cap>e.flow && d[v]>d[u]+e.cost) {
44                     d[v]=d[u]+e.cost;
45                     p[v]=G[u][i];
46                     a[v]=min(a[u],e.cap-e.flow);        //min(a[u],..)
47                     if(!inq[v]) { inq[v]=1; q.push(v);
48                     }
49                 }
50             }
51         }
52         if(d[t]==INF) return false;
53         if(flow+a[t] > flowlimit) a[t] = flowlimit-flow;
54         flow+=a[t] , cost+=a[t]*d[t];
55         for(int x=t; x!=s; x=es[p[x]].u) {
56             es[p[x]].flow+=a[t]; es[p[x]^1].flow-=a[t];
57         }
58         return true;
59     }
60     int Mincost(int s,int t,int flowlimit,int& cost) {
61         int flow=0; cost=0;
62         while(flow<flowlimit && SPFA(s,t,flowlimit,flow,cost)) ;
63         return flow;
64     }
65 } mc;
66
67 int n,m;
68
69 int main() {
70     while(scanf("%d%d",&n,&m)==2) {
71         mc.init(n+n);
72         int u,v,w;
73         FOR(i,0,m) {
74             scanf("%d%d%d",&u,&v,&w);
75             u--,v--;
76             mc.AddEdge(n+u,v,1,w);
77         }
78         FOR(i,0,n) mc.AddEdge(i,n+i,1,0);
79         int cost,flow;
80         flow=mc.Mincost(n+0,n-1,2,cost);
81         printf("%d\n",cost);
82     }
83     return 0;
84 }
时间: 2024-10-14 20:42:14

UVa1658 Admiral(拆点法+最小费用流)的相关文章

UVa1658 Admiral (拆点法,最小费用流)

链接:http://vjudge.net/problem/UVA-1658 分析:把2到v-1的每个节点i拆成i和i'两个结点,中间连一条容量为1,费用为0的边,然后求1到v的流量为2的最小费用流即可. 1 #include <cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<vector> 5 #include<algorithm> 6 using namespace std; 7

UVA1658 Admiral 拆点法解决结点容量(路径不能有公共点,容量为1的时候) 最小费用最大流

/** 题目:UVA1658 Admiral 链接:https://vjudge.net/problem/UVA-1658 题意:lrj入门经典P375 求从s到t的两条不相交(除了s和t外,没有公共点)的路径,使得权值和最小. 思路:拆点法. 除了s,t外.把其他点都拆成两个. 例如点A,拆成A和A'.A指向A'连一条容量为1,花费为0的边. 原来指向A的,仍然指向A点. 原来A指向其他点的,由A'指向它们. 最小费用最大流求流量为2时候的最小费用即可. */ #include<iostrea

UVa 1658 (拆点法 最小费用流) Admiral

题意: 给出一个有向带权图,求从起点到终点的两条不相交路径使得权值和最小. 分析: 第一次听到“拆点法”这个名词. 把除起点和终点以外的点拆成两个点i和i',然后在这两点之间连一条容量为1,费用为0的边.这样就保证了每个点最多经过一次. 其他有向边的容量也是1 然后求从起点到终点的流量为2(这样就保证了是两条路径)的最小费用流. 本来要在加一个源点和汇点来限制流量的,但是这样弧就多了很多.lrj代码中用了很巧妙的方法,避免了这个问题. 1 #include <bits/stdc++.h> 2

1658 - Admiral (拆点+最小费用流)

该题中的拆点法是解决几点容量的通用方法 .  因为只有容量限制的话仍然不能满足每个结点只访问一次这个限制 ,原因很简单,大家画个图就知道了,假设从起点有两条路到同一个结点2,然后又都到末点n,虽然它们满足流量限制但是经过了同一个结点. 那么怎么解决这个问题呢? 答案是:拆点法 . 将一个结点拆成两个结点,由真结点连一条容量为1费用为0的边到假结点,这样之后当我们加边的时候,另起始结点为假结点,终止点为真结点.这样就将这个结点隐性的增加了一个容量属性 . 当然,由于我们要经过起始点和末点两次,所以

uva1658 admiral

费用流. 裸的拆点最小费用流,一跑就行. 核弹预警,为何wa20多发.build函数一定要返回true...... 太可怕了 #include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; const int maxn = 5000 + 10 ; const int maxm = 200000 + 10; const int inf = 0

Risk UVA - 12264 拆点法+最大流+二分

/** 题目:Risk UVA - 12264 链接:https://vjudge.net/problem/UVA-12264 题意:给n个点的无权无向图(n<=100),每个点有一个非负数ai. 若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵. 保证至少有一个属于你的点与敌方的点相邻.你可以让你的每个士兵最多移动一次 ,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵, 使得最薄弱的关口尽量坚固.关口是指与敌方点相邻的点,薄弱与坚固分别指兵少

UVA1349 Optimal Bus Route Design 拆点法+最小费用最佳匹配

/** 题目:UVA1349 Optimal Bus Route Design 链接:https://vjudge.net/problem/UVA-1349 题意:lrj入门经典P375 给n个点(n<=100)的有向带权图,找若干个有向圈,每个点恰好属于一个圈.要求权和尽量小.注意即使(u,v) 和(v,y)都存在,他们的权值也不一定相同. 思路:拆点法+最小费用最佳完美匹配. 如果每个点都有一个唯一的后继(不同的点没有相同的后继点,且只有一个后继),那么每个点一定恰好属于一个圈. 联想到二分

poj3422 拆点法x-&gt;x&#39;建立两条边+最小费用最大流

/** 题目:poj3422 拆点法+最小费用最大流 链接:http://poj.org/problem?id=3422 题意:给定n*n的矩阵,含有元素值,初始sum=0.每次从最左上角开始出发,每次向右或者向下一格.终点是右下角. 每经过一个格子,获取它的值,并把该格子的值变成0.问经过k次从左上角到右下角.能得到的数值和最大多少. 思路:我觉得本题元素值全是非负数.要不然不可以过.很多网上的博客代码在有负数情况下过不了. 拆点法+最小费用最大流 建图: 每一个格子x,拆成x,xi, x向x

HDU 2732 Leapin&#39; Lizards(拆点法+最大流)

该题是一道比较简单拆点+最大流的题目,因为每个柱子都有一定的寿命,很容易将其对应成流量,那么处理结点容量的一般方法当然是拆点法 .该题反而对边的容量没有要求,为保险起见可以设成无穷大.   该题的思路很好想,建议独立编写代码 . 推荐题目: 点击打开链接    结点法的一些见解 也可以看这里. 细节参见代码: #include<bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 100000000;