1.Edmonds-Karp 最短增广路算法
算法的时间复杂度上限为O(n*m^2),(n是顶点数,m是边数)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxm=210;
const int INF=0x3f3f3f3f;
int mp[maxm][maxm];
int n,m;
int pre[maxm]; //路径上每个节点的前驱节点
bool vis[maxm];
unsigned Augment(){
deque<int> q;
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
pre[1]=0;
vis[1]=1;
q.push_back(1);
bool bFindPath=false;
//用bfs寻找一条从源到汇的可行路径
int v;
while(!q.empty()){
v=q.front();
q.pop_front();
for(int i=1;i<=m;i++){
if(mp[v][i]>0&&!vis[i]){
pre[i]=v;
vis[i]=1;
if(i==m){
bFindPath=true;
q.clear();
break;
}else q.push_back(i);
}
}
}
if(!bFindPath) return 0;
int nMinFlow=INF;
v=m;
//寻找源到汇路径上流量最小的边,其容量就是此次增加的总流量
while(pre[v]){
nMinFlow=min(nMinFlow,mp[pre[v]][v]);
v=pre[v];
}
//沿此路径添加反向边,同时修改路径上每条边的容量
v=m;
while(pre[v]){
mp[pre[v]][v]-=nMinFlow;
mp[v][pre[v]]+=nMinFlow;
v=pre[v];
}
return nMinFlow;
}
int main(){
while(~scanf("%d%d",&n,&m)){ //m是顶点数目,编号从1开始
memset(mp,0,sizeof(mp));
int s,e,c;
for(int i=0;i<n;i++){
scanf("%d%d%d",&s,&e,&c);
mp[s][e]+=c; //两点之间可能存在多条边
}
unsigned int MaxFlow=0;
unsigned int aug;
while(aug=Augment()){
// printf("aug=%d\n",aug);
MaxFlow+=aug;
}
printf("%d\n",MaxFlow);
}
return 0;
}
2.Dinic快速网络流算法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int G[300][300];
bool visited[300];
int Layer[300];
int n,m; //1是源点,m是汇点
bool CountLayer(){
int layer=0;
deque<int> q;
memset(Layer,0xff,sizeof(Layer)); //都初始化为-1
Layer[1]=0;
q.push_back(1);
while(!q.empty()){
int v=q.front();
q.pop_front();
for(int i=1;i<=m;i++){
if(G[v][i]>0&&Layer[i]==-1){ //Layer[i]==-1说明i还没有访问过
Layer[i]=Layer[v]+1;
if(i==m) //分层到汇点即可
return true;
else
q.push_back(i);
}
}
}
return false;
}
int Dinic(){
int s;
int nMaxFlow=0;
deque<int> q; //DFS用的栈
while(CountLayer()){ //只要能分层
q.push_back(1); //汇点入栈
memset(visited,0,sizeof(visited));
while(!q.empty()){
int nd=q.back();
if(nd==m){ //nd是汇点
int nMinC=INF; //在栈中找到容量最小边
int nMinC_vs; //容量最小边的起点
for(int i=1;i<q.size();i++){
int vs=q[i-1];
int ve=q[i];
if(G[vs][ve]>0){
if(nMinC>G[vs][ve]){
nMinC=G[vs][ve];
nMinC_vs=vs;
}
}
}
//增广,改图
nMaxFlow+=nMinC;
for(int i=1;i<q.size();i++){
int vs=q[i-1];
int ve=q[i];
G[vs][ve]-=nMinC; //修改边容量
G[ve][vs]+=nMinC; //添加反向边
}
//退栈到nMinC_vs成为栈顶,以便继续dfs
while(!q.empty()&&q.back()!=nMinC_vs){
visited[q.back()]=0;
q.pop_back();
}
}
else{ //nd不是汇点
int i;
for(i=1;i<=m;i++){
if(G[nd][i]>0&&Layer[i]==Layer[nd]+1&&!visited[i]){
//只往下一层没有走过的结点走
visited[i]=1;
q.push_back(i);
//cout<<"nd="<<nd<<",i="<<i<<endl;
break;
}
}
if(i>m) //找不到下一个点
q.pop_back(); //回溯
}
}
}
return nMaxFlow;
}
int main(){
while(~scanf("%d%d",&n,&m)){
int s,e,c;
memset(G,0,sizeof(G));
for(int i=0;i<n;i++){
scanf("%d%d%d",&s,&e,&c);
G[s][e]+=c; //两点之间可能有多条边
}
printf("%d\n",Dinic());
}
return 0;
}
题意:题意本身就比较难理解,静下心,慢慢看~
有n台机器组装电脑,电脑由p个部件组成,每台
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int G[110][110],tag[110][110]; //tag[i][j]表示从i到j流出的流量,如果值为负数,说明是流出,方便后面计算
bool visited[110];
int Layer[110];
int n,s,t;
bool CountLayer(){
int layer=0;
deque<int> q;
memset(Layer,0xff,sizeof(Layer)); //都初始化为-1
Layer[0]=0;
q.push_back(0);
while(!q.empty()){
int v=q.front();
q.pop_front();
for(int i=0;i<=t;i++){
if(G[v][i]>0&&Layer[i]==-1){ //Layer[i]==-1说明i还没有访问过
Layer[i]=Layer[v]+1;
if(i==t) //分层到汇点即可
return true;
else
q.push_back(i);
}
}
}
return false;
}
int Dinic(){
int nMaxFlow=0;
deque<int> q; //DFS用的栈
while(CountLayer()){ //只要能分层
q.push_back(s); //源点入栈
memset(visited,0,sizeof(visited));
while(!q.empty()){
int nd=q.back();
if(nd==t){ //nd是汇点
int nMinC=INF; //在栈中找到容量最小边
int nMinC_vs; //容量最小边的起点
for(int i=1;i<q.size();i++){
int vs=q[i-1];
int ve=q[i];
if(G[vs][ve]>0){
//if(vs>n&&vs-n!=ve&&ve!=s||ve>n&&ve-n!=vs&&vs!=s) printf("vs=%d,ve=%d\n",vs,ve);
if(nMinC>G[vs][ve]){
nMinC=G[vs][ve];
nMinC_vs=vs;
}
}
}
//增广,改图
nMaxFlow+=nMinC;
for(int i=1;i<q.size();i++){
int vs=q[i-1];
int ve=q[i];
G[vs][ve]-=nMinC; //修改边容量
G[ve][vs]+=nMinC; //添加反向边
tag[vs][ve]-=nMinC;
tag[ve][vs]+=nMinC;
}
//退栈到nMinC_vs成为栈顶,以便继续dfs
while(!q.empty()&&q.back()!=nMinC_vs){
visited[q.back()]=0;
q.pop_back();
}
}
else{ //nd不是汇点
int i;
for(i=0;i<=t;i++){
if(G[nd][i]>0&&Layer[i]==Layer[nd]+1&&!visited[i]){
//只往下一层没有走过的结点走
visited[i]=1;
q.push_back(i);
//cout<<"nd="<<nd<<",i="<<i<<endl;
break;
}
}
if(i>t) //找不到下一个点
q.pop_back(); //回溯
}
}
}
return nMaxFlow;
}
int main(){
int p,w[55],in[55][15],out[55][15];
while(~scanf("%d%d",&p,&n)){
memset(G,0,sizeof(G));
memset(tag,0,sizeof(tag));
s=0;
t=2*n+1;
bool flag;
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
flag=true;
for(int j=1;j<=p;j++){
scanf("%d",&in[i][j]);
if(in[i][j]==1) flag=false; //判断是否与源点s连边
}
if(flag)
G[s][i]=INF;
flag=true;
for(int j=1;j<=p;j++){
scanf("%d",&out[i][j]);
if(out[i][j]==0) flag=false; //判断是否与汇点t连边
}
if(flag)
G[i+n][t]=INF;
}
for(int i=1;i<=n;i++){
G[i][i+n]=w[i]; //内部连边
for(int j=1;j<=n;j++){
flag=true;
if(i==j) continue;
for(int k=1;k<=p;k++){
if(in[j][k]!=2&&out[i][k]!=in[j][k]){
flag=false;
break;
}
}
if(flag){
G[i+n][j]=INF; //机器之间相连
}
}
}
printf("%d ",Dinic());
int cnt=0;
for(int i=1;i<t;i++){
for(int j=1;j<t;j++){
if(i==j+n||j==i+n) continue; //不是内部边、与源点相连的边、与汇点相连的边
if(tag[i][j]<0) //值为负,说明有流量从i点流到j点,即对最大流有贡献
cnt++;
}
}
printf("%d\n",cnt);
for(int i=1;i<t;i++){
for(int j=1;j<t;j++){
if(i==j+n||j==i+n) continue; //不是内部边、与源点相连的边、与汇点相连的边
if(tag[i][j]<0){
printf("%d %d %d\n",i==n?i:i%n,j==n?j:j%n,tag[i][j]*(-1));
}
}
}
}
return 0;
}