hdu 3667 Transportation(最小费用最大流+拆边)

2023-12-28 07:08

本文主要是介绍hdu 3667 Transportation(最小费用最大流+拆边),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意:

求从城市1运送K单位物品到城市n的最小花费。给定的有向边,每条边都有其容量c,并且,产生的费用是 a * ( f * f ),其中f是这条边上的流量,a是给出的系数。

思路:

这个题目就是刘汝佳训练指南上建模与建图的一种,费用与流量的平方成正比的最小流。容量c均为整数,并且每条弧还有一个费用系数a,表示该弧流量为x时费用为a*x*x,如何求最小费用最大流?

用拆边法,如图

图更正:   最后一个cost=9a,(图有点丑)

一个费用系数为a,容量为5的边被拆成5条容量为1费用各异的弧,因为求的是最小费用流,如果这条弧的流量为1,走的肯定是cost=1的那条弧;如果流量为2,肯定走的是cost=1和cost=3的那两条;如果流量为3,走的肯定是 cost=1,3,5那三条。。。。。。不难验证,不管流量是1~5中间的哪一个,左右两个图都是等价的。这样就转化为普通的最小费用最大流问题。需要注意的是,因为有重边,普通的邻接矩阵无法满足要求。要么采用模板 最小费用最大流(刘汝佳 p363),要么采用把邻接矩阵加一维,表示某两点间的第几条弧。

此题分析:给定一条边(u,v),其计费系数为a,容量为c,那么可以把(u,v)拆成5条边,费用为(1a,3a,5a,7a,9a),容量都为1,。对所有输入的有向边,按照上述方法拆边建图,然后加入超级源点s(0),s连向1,容量为K,费用为0。然后跑一遍最小费用最大流,若流量不等于K,则输出-1,否则输出最小费用。

代码:

#include<map>  
#include<set>  
#include<cmath>  
#include<queue>  
#include<stack>  
#include<ctime>  
#include<cctype>  
#include<string>  
#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<iostream>  
#include<algorithm>  
using namespace std;  #define end() return 0  typedef long long ll;  
typedef unsigned int uint;  
typedef unsigned long long ull;  const int maxn = 100 + 5;  
const int INF = 0x7f7f7f7f;  struct Edge{  int from,to,cap,flow,cost;  Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}  
};  struct MCMF{  int n,m,flow,cost;  vector<Edge>edge; //边数的两倍  vector<int>G[maxn]; //邻接表,G[i][j]表示i的第j条边在e数组中的序号  int inq[maxn]; //是否在队列  int d[maxn]; //Bellman-Ford  int p[maxn]; //上一条弧  int a[maxn]; //可改进量  void init(int n){  this -> n = n;  for(int i=0;i<=n;i++) G[i].clear();  edge.clear();  }  void addEdge(int from,int to,int cap,int cost){  edge.push_back(Edge(from,to,cap,0,cost));  edge.push_back(Edge(to,from,0,0,-cost));  m=edge.size();  G[from].push_back(m-2);  G[to].push_back(m-1);  }  bool BellmanFord(int s,int t,int& flow,int& cost){  memset(d,INF,sizeof(d));  memset(inq,0,sizeof(inq));  d[s]=0; inq[s]=1; p[s]=0; a[s]=INF;  queue<int>q;  q.push(s);  while(!q.empty()){  int u=q.front();q.pop();  inq[u]=0;  for(int i=0;i<G[u].size();i++){  Edge& e=edge[G[u][i]];  if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){  d[e.to]=d[u]+e.cost;  p[e.to]=G[u][i];  a[e.to]=min(a[u],e.cap-e.flow);  if(!inq[e.to]){  q.push(e.to);  inq[e.to]=1;  }  }  }  }  if(d[t]==INF) return false;  flow+=a[t];  cost+=d[t]*a[t];  for(int u=t;u!=s;u=edge[p[u]].from){  edge[p[u]].flow+=a[t];  edge[p[u]^1].flow-=a[t];  }  return true;  }  //需要保证初始网络中没有负权圈  void MincostMaxflow(int s,int t){  flow=0,cost=0;  while(BellmanFord(s,t,flow,cost));  }  
};  struct EDGE{  int u,v,a,c;  EDGE(){}  EDGE(int u,int v,int a,int c):u(u),v(v),a(a),c(c){}  
};  int N,M,K;  
int u,v,a,c;  
EDGE edges[5005];  
MCMF mcmf;  void input(){  for(int i=1;i<=M;i++){  scanf("%d%d%d%d",&u,&v,&a,&c);  edges[i]=EDGE(u,v,a,c);  }  
}  void createGraph(){  mcmf.init(N+1);  mcmf.addEdge(0,1,K,0);  for(int k=1;k<=M;k++){  int x=edges[k].c*edges[k].c;  for(int i=1,j=1;j<=x;i+=2,j+=i){  mcmf.addEdge(edges[k].u,edges[k].v,1,edges[k].a*i);  }  }  
}  void solve(){  createGraph();  mcmf.MincostMaxflow(0,N);  if(mcmf.flow==K) printf("%d\n",mcmf.cost);  else printf("-1\n");  
}  int main(){  while(scanf("%d%d%d",&N,&M,&K)!=EOF){  input();  solve();  }return 0;  
}  




这篇关于hdu 3667 Transportation(最小费用最大流+拆边)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/545295

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl