链接:https://vjudge.net/problem/POJ-3281
题意:
一头牛需要吃1个drink和1个food,给定牛的喜好,每个food和drink只能给1头牛吃,问最多满足多少头牛
解析:
匹配问题,第一个想到是二分图匹配,但是这里要匹配两个东西,所以二分图匹配无效
解法为最大流.
如果建图为:food->牛->drink,解决了一个食物饮料给1头牛吃,但是无法解决牛吃多个食物,多个drink
正确的建图为:food->牛->牛'->drink,这样就保证牛只会吃一个food和drink
网络流建图才是关键
0,2*n+f+d+1分别为源点和汇点
1~f为food, f+1~2*n+f为牛与niu', 2*n+f+1~2*n+f+d为drink.
ac:
#include <cstdio>
#include<iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 1005;
const int inf = 1e9 + 7;
int G[MAXN][MAXN],layer[MAXN], G1[MAXN][MAXN];
int P, N;
struct node
{
int in[MAXN],
out[MAXN],
flow;
}a[MAXN];
void init()
{
memset(G, 0, sizeof G);
memset(G1, 0 ,sizeof G1);
for (int i = 1; i <= P; i++)
{
a[1].out[i] = 0;
a[1].in[i] = 0;
a[N + 2].in[i] = 1;
a[N + 2].out[i] = 1;
}
a[1].flow = inf;
a[N + 2].flow = inf;
}
bool BFS(int front, int tail) {
int vis[MAXN] = {0};
queue<int> que;
que.push(front);
memset(layer,-1,sizeof(layer));
vis[front] = 1;
layer[front] = 0;
while (!que.empty()){
int u=que.front();
que.pop();
if (u == tail) return true;
for(int i=1;i<=tail;i++){
if (G[u][i] && !vis[i]){
vis[i] = true;
layer[i] = layer[u] + 1;
que.push(i);
}
}
}
return false;
}
int dfs(int u, int MaxFlow, int tail){
if (u == tail) return MaxFlow;
int uFlow = 0;
for (int i = 0; i<= tail; i++){
if (layer[u] + 1 == layer[i] && G[u][i]){
int flow = min(MaxFlow - uFlow , G[u][i]);
flow = dfs(i, flow, tail);
G[u][i] -= flow;
G[i][u] += flow;
uFlow += flow;
if (uFlow == MaxFlow) break;
}
}
return uFlow ;
}
int dinic(int front, int tail){
int MaxFlow = 0;
while (BFS(front, tail))
MaxFlow += dfs(front, inf, tail);
return MaxFlow;
}
int main()
{
int n,f,d,x,y,c;
scanf("%d%d%d",&n,&f,&d);
int st=0,ed=f+2*n+d+1;
for(int i=1;i<=f;i++)
G[st][i]=1;
for(int i=1;i<=n;i++)
G[i+f][i+n+f]=1;
for(int i=1;i<=d;i++)
G[2*n+f+i][ed]=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
while(x--)
{
scanf("%d",&c);
G[c][f+i]=1;
}
while(y--)
{
scanf("%d",&c);
G[i+n+f][2*n+f+c]=1;
}
}
printf("%d\n",dinic(st,ed));
}
链接:https://codeforces.com/gym/101981/attachments
题意:
有n个英雄,m个野怪,k瓶药水
1个英雄只能打1个野怪
野怪只能被打死1次
k瓶药,可以让一个英雄额外打死一个野怪
一个英雄最多吃1瓶药
问最多打死几个野怪?
解析:
最大流问题,模板题
建图方法:
源点->(1)英雄->(2)英雄'->(1)野怪->(1)野怪'->(1)终点
源点->(k)药->(1)英雄
ac:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define MAXN 2100
using namespace std;
/* n,m,k 英雄数目,野怪数,药水数
ti ti个mij
*/
int G[MAXN][MAXN];
const int inf = 1e9 + 7;
int layer[MAXN], G1[MAXN][MAXN];
int P, N;
struct node
{
int in[MAXN],
out[MAXN],
flow;
}a[MAXN];
void init()
{
memset(G, 0, sizeof G);
memset(G1, 0 ,sizeof G1);
for (int i = 1; i <= P; i++)
{
a[1].out[i] = 0;
a[1].in[i] = 0;
a[N + 2].in[i] = 1;
a[N + 2].out[i] = 1;
}
a[1].flow = inf;
a[N + 2].flow = inf;
}
bool BFS(int front, int tail) {
int vis[MAXN] = {0};
queue<int> que;
que.push(front);
memset(layer,-1,sizeof(layer));
vis[front] = 1;
layer[front] = 0;
while (!que.empty()){
int u=que.front();
que.pop();
if (u == tail) return true;
for(int i=1;i<=tail;i++){
if (G[u][i] && !vis[i]){
vis[i] = true;
layer[i] = layer[u] + 1;
que.push(i);
}
}
}
return false;
}
int dfs(int u, int MaxFlow, int tail){
if (u == tail) return MaxFlow;
int uFlow = 0;
for (int i = 0; i<= tail; i++){
if (layer[u] + 1 == layer[i] && G[u][i]){
int flow = min(MaxFlow - uFlow , G[u][i]);
flow = dfs(i, flow, tail);
G[u][i] -= flow;
G[i][u] += flow;
uFlow += flow;
if (uFlow == MaxFlow) break;
}
}
return uFlow ;
}
int dinic(int front, int tail){
int MaxFlow = 0;
while (BFS(front, tail))
MaxFlow += dfs(front, inf, tail);
return MaxFlow;
}
int main()
{
int n,m,k,g,t;
scanf("%d%d%d",&n,&m,&k);
int st=0;//起点
int ed=2*n+2*m+2;///终点(终点一定为坐标最大的)
int gv=ed-1;//药水点
///建图
G[st][gv]=k;//连药水
for(int i=1;i<=n;i++)//英雄连英雄',源点连英雄,药水连英雄
{
G[st][i]=1;
G[i][i+n]=2;
G[gv][i]=1;
}
for(int i=1;i<=m;i++)//野怪连野怪'
{
G[2*n+i][2*n+m+i]=1;
G[2*n+m+i][ed]=1;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
for(int j=1;j<=t;j++)
{
scanf("%d",&g);
G[n+i][2*n+g]=1;
}
}
printf("%d\n",dinic(st,ed));
return 0;
}
hdu-6582:http://acm.hdu.edu.cn/showproblem.php?pid=6582
解析:
给定一个图,让你切断一些边,使得最短路变大或者断路
解析:
用最短路点建图求最小割
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
struct Edge
{
ll v, w, nxt;
};
bool isFind;
ll head[MAXN];
Edge edge[MAXN<<1];
ll dis[MAXN], gap[MAXN];
ll n, m, ecnt, aug, maxFlow;
ll u[MAXN],v[MAXN],w[MAXN];
ll to[MAXN<<1],val[MAXN<<1],next1[MAXN<<1],head2[MAXN<<1];
ll tot=0;
ll dist[MAXN];
void init(){
tot=ecnt = maxFlow = 0;
memset(gap, 0, sizeof(gap));
memset(dis, 0, sizeof(dis));
memset(edge, 0, sizeof(edge));
memset(head, -1, sizeof(head));
memset(dist,0,sizeof(dist));
memset(to,0,sizeof(to));
memset(head2,0,sizeof(head2));
memset(val,0,sizeof(val));
memset(next1,0,sizeof(next1));
gap[0] = n;
}
void addEdge(ll u, ll v, ll w){
edge[ecnt].v = v;
edge[ecnt].w = w;
edge[ecnt].nxt = head[u];
head[u] = ecnt++;
}
void Find(ll s)
{
ll dx, augc;
ll minDis;
if(s == n){
isFind = true;
maxFlow += aug;
return;
}
augc = aug;
minDis = n - 1;
for(ll i = head[s]; i + 1; i = edge[i].nxt){
if(edge[i].w > 0){
if(dis[s] == dis[edge[i].v] + 1){
aug = min(aug, edge[i].w);
Find(edge[i].v);
if(dis[1] >= n) return;
if(isFind){
dx = i;
break;
}
aug = augc;
}
minDis = min(minDis, dis[edge[i].v]);
}
}
if(!isFind){
gap[dis[s]]--;
if(gap[dis[s]] == 0) dis[1] = n;
dis[s] = minDis + 1;
gap[dis[s]]++;
}else{
edge[dx].w -= aug;
edge[dx ^ 1].w += aug;
}
}
struct node
{
ll to;
ll dis;
friend bool operator <(node a,node b)
{
return a.dis>b.dis;
}
};
void add(ll u,ll v,ll x)
{
to[++tot]=v;
val[tot]=x;
next1[tot]=head2[u];
head2[u]=tot;
}
void dij(ll st)
{
for(ll i=0;i<MAXN;i++)
dist[i]=1e18;
dist[st]=0;
priority_queue<node> que;
que.push(node{st,0});
while(!que.empty())
{
ll u=que.top().to;que.pop();
for(ll i=head2[u];i;i=next1[i])
{
ll v=to[i];
if(dist[v]>dist[u]+val[i])
{
dist[v]=dist[u]+val[i];
que.push(node{v,dist[v]});
}
}
}
}
int main()
{
ll t;
scanf("%lld",&t);
while(t--)
{
init();
scanf("%lld%lld", &n, &m);
init();
for(ll i = 0; i < m; i++)
{
scanf("%lld%lld%lld", &u[i], &v[i], &w[i]);
add(u[i],v[i],w[i]);
}
dij(1);
for(ll i=0;i<m;i++)
{
if(dist[v[i]]==dist[u[i]]+w[i])
{
addEdge(u[i],v[i],w[i]);
addEdge(v[i],u[i],0);
}
}
while(dis[1]<n)
{
isFind = 0;
aug = 1e18;
Find(1);
}
printf("%lld\n",maxFlow);
}
return 0;
}
最大权闭合子图:
链接:https://ac.nowcoder.com/acm/contest/116/D
众所周知,杨老师是一位十分勤奋的老师,他非常的热爱学习。
勤奋的他为自己罗列了一个学习清单,共有n个知识点,他可以有选择的进行学习。
每个知识点都会对应0个或1个或多个先修知识点(只有学会了先修知识点才能学习该知识点),同时每个知识点都有一个智慧值和一个智力消耗值。
杨老师希望在进行过激烈的学习之后,他的收获可以·量化为所有学过的题的智慧值的和与智力消耗值的和的差值。请问,这个值最大是多少?
解析:
最大权闭合子图
经典的费用流问题,什么是最大权闭合子图呢?就是给你一个带权图,我们要取出一个连通块使之权值最大
杨老师获得的智慧-智力消耗最大=>全图中的正权点-最小割
ac:
#include<bits/stdc++.h>
#define mm1(a) memset((a),-1,sizeof((a)))
#define mm0(a) memset((a),0,sizeof((a)))
#define mmx(a) memset((a),0x3f,sizeof((a)))
using namespace std;
typedef long long LL; //https://blog.csdn.net/qq_39599067/article/details/80178357
const int N = 100005;
const int INF = 0x3f3f3f3f;
int n,m,tot,vt,vs;
int d[N],head[N];
struct lp
{
int to,w,nex;
lp(){}
lp(int a,int b,int c){to=a;w=b;nex=c;}
}cw[N<<2];
int pigNum[N],pigTo[N];
inline void add(int a,int b,int c)
{
cw[++tot]=lp(b,c,head[a]);
head[a]=tot;
cw[++tot]=lp(a,0,head[b]);
head[b]=tot;
}
bool bfs()
{
mm1(d);
queue<int>Q;
Q.push(vt);d[vt]=0;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for(int i=head[u];i!=-1;i=cw[i].nex)
{
int v=cw[i].to;
if(cw[i^1].w&&d[v]==-1)
{
d[v]=d[u]+1;
Q.push(v);
}
}
}
return d[vs]!=-1;
}
int dfs(int x,int f)
{
if(x==vt||f==0) return f;
int use=0,w;
for(int i=head[x];i!=-1;i=cw[i].nex)
{
int to=cw[i].to;
if(d[to]==d[x]-1 && cw[i].w)
{
w=dfs(to,min(cw[i].w,f-use));
cw[i].w-=w,cw[i^1].w+=w;
use+=w;
if(use==f) return f;
}
}
return use;
}
inline void init()
{
tot=-1;
mm1(head);
mm0(pigTo);
vs=0,vt=n+1;
}
int main()
{
int x,y;
scanf("%d",&n);
init();
int sum=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&x,&y);
if(x-y>=0)
{
sum+=x-y;
add(vs,i,x-y);
}
else{
add(i,vt,y-x);
}
}
int cc=0;
while(~scanf("%d%d",&x,&y))
{
add(y,x,INF);
cc++;
if(cc==3)
break;
}
int ans=0;
while(bfs())
ans+=dfs(vs,INF);
printf("%d\n",sum-ans);
return 0;
}
https://www.cnblogs.com/wangwangyu/p/9802513.html
费用流:https://blog.csdn.net/weixin_41183791/article/details/88956873