原理就是内个原理~
最大流==最小割==二分图最小点权覆盖权值和
dc+弧优化
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
typedef long long LL;
const int N = 20005;
const int INF = 0x3f3f3f3f;
bool vis[N];
struct Edge{
int to, cap, flow, next;
}edge[N*50];
int n, m, cnt;//n是点 m是边 cnt是加边操作后的边
int head[N];//邻接表
int dis[N];//分层 等级
int cur[N];//弧优化
void add(int u, int v, int w){
edge[cnt] = (struct Edge){v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = (struct Edge){u, 0, 0, head[v]};
head[v] = cnt++;
}
bool bfs(int start, int endd){//分层
memset(dis, -1, sizeof(dis));
memset(vis, false, sizeof(vis));
queue<int>que;
dis[start] = 0;
vis[start] = true;
que.push(start);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next){
Edge E = edge[i];
if(!vis[E.to] && E.flow<E.cap){
dis[E.to] = dis[u]+1;
vis[E.to] = true;
if(E.to == endd) return true;
que.push(E.to);
}
}
}
return false;
}
int dfs(int x, int res, int endd){ //增广
if(x == endd || res == 0) return res;
int flow = 0, f;
for(int& i = cur[x]; i != -1; i = edge[i].next){
Edge E = edge[i];
if(dis[E.to] == dis[x]+1){
f = dfs(E.to, min(res, E.cap-E.flow), endd);
if(f>0){
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
res -= f;
if(res == 0) break;
}
}
}
return flow;
}
int max_flow(int start, int endd){
int flow = 0;
while(bfs(start, endd)){
memcpy(cur, head, sizeof(head));//初始化弧优化数组
flow += dfs(start, INF, endd);
}
return flow;
}
void init(){//初始化
cnt = 0;
memset(head, -1, sizeof(head));
}
int main(){
scanf("%d%d", &m, &n);
init();
int sp=1;//源点
int tp=n;//汇点
for(int i = 1; i <= m; i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x, y, z);
add(y, x, z);
}
int ans = max_flow(sp, tp);
printf("%d\n", ans);
return 0;
}
费用流板子:
/*
最小费用最大流模版.求最大费用最大流建图时把费用取负即可。
无向边转换成有向边时需要拆分成两条有向边。即两次加边。
可处理负边
不能处理负环
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1000000;
const int inf=0x3f3f3f3f;
int head[maxn];//邻接表
int d[maxn];//最短距离 dis
int s,e,no;
int vis[maxn];//标记数组
int pre[maxn];//记录前驱结点 方便 “反悔”
struct point{
int u,v,flow,next,cost;
point(){};
point(int x,int y,int z,int w,int c):u(x),v(y),next(z),flow(w),cost(c){};
}p[maxn];
void add(int x,int y,int z,int c){
p[no]=point(x,y,head[x],z,c);
head[x]=no++;
p[no]=point(y,x,head[y],0,-c); //加负边
head[y]=no++;
}
void init(){
memset(head,-1,sizeof(head));
no=0;
}
bool spfa(){
int i,x,y;
queue<int>q;
memset(d,0x3f,sizeof(d));
memset(vis,false,sizeof(vis));
memset(pre,-1,sizeof(pre));
d[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty()){
x=q.front();
q.pop();
vis[x]=false;
for(i=head[x];i!=-1;i=p[i].next){
if(p[i].flow&&d[y=p[i].v]>d[x]+p[i].cost){
d[y]=d[x]+p[i].cost;
pre[y]=i;
if(vis[y])
continue;
vis[y]=true;
q.push(y);
}
}
}
return d[e]!=d[e+1];
}
int mcmf(){
int mincost=0,maxflow=0,minflow,i;
while(spfa()){
minflow=inf;
for(i =pre[e];i!=-1;i=pre[p[i].u])
minflow=min(minflow,p[i].flow);
for(i=pre[e];i!=-1;i=pre[p[i].u]){
p[i].flow-=minflow;
p[i^1].flow+=minflow;
}
mincost+=d[e]*minflow;
maxflow+=minflow;
}
//跑完所有最短增广路
if(maxflow==2)//判断是否满流 (判断最大流等于2) 则有两条路
return mincost;
return -1;
}
int n,m;
int main(){
int res=1;//输出 Case :的
while(scanf("%d%d",&n,&m),n||m){
init();//初始化
s=0;//源点为0
e=n+1;//汇点为N+1
add(s,1,2,0);//源点向点1件容量为2,花费为0的边
add(n,e,2,0);//点N向汇点建容量为2,花费为0的边
while(m--){
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u+1,v+1,1,c);//每条边的起点和终点建容量为1,花费为边权的的边
// add(v,u,1,c)//无向图加这个 把无向图拆成两个方向不同的有向图
}
int ans=mcmf();
printf("Instance #%d: ",res++);
if(ans==-1)//不满流
printf("Not possible\n");
else//满流则输出最小花费
printf("%d\n",ans);
}
return 0;
}