訓練指南 網絡流題集
layout: post
title: 訓練指南 網絡流題集
author: "luowentaoaa"
catalog: true
mathjax: true
tags:
- 網絡流
- 圖論
- 訓練指南
A.UVA - 11248 (最大流,最小割)
UVA - 11248 Frequency Hopping
題意
給定一個有向網絡,每條邊均有一個容量。問是否存在一個從點1到點N,流量為C的流。如果不存在,是否可以恰好修改一條弧的容量,使得存在這樣的流。
思路
先求一遍最大流,如果大於等於C,那麽就直接輸出possible。
否則的話就是最大流達不到C,那麽對哪些邊進行擴容呢,肯定是選擇最小割!
將最小割的邊集全部求出來,之後每條邊都嘗試將容量變為C,看看能否達到要求。
優化一:求完最大流後把流量留著,以後每次在它的基礎上增廣。
優化二:每次沒必要求出最大流,增廣到流量至少為C時就可以停下來。
搞不到為什麽劉汝佳代碼那麽快
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=998244353; const int maxn=1e6+50; const ll inf=0x3f3f3f3f3f3f3f3fLL; struct Edge{ int from,to,cap,flow; Edge(int u,int v,int c,int f) :from(u),to(v),cap(c),flow(f){} bool operator<(const Edge& a)const{ return from<a.from||(from==a.from&&to<a.to); } }; struct Dinic{ int n,m,s,t; vector<Edge>edges; vector<int>G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; void init(int n){ this->n=n; for(int i=0;i<n;i++)G[i].clear(); edges.clear(); } void ClearFlow(){ for(int i=0;i<edges.size();i++)edges[i].flow=0; } void AddEdge(int from,int to,int cap){ edges.push_back(Edge(from,to,cap,0)); edges.push_back(Edge(to,from,0,0)); m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS(){ memset(vis,0,sizeof(vis)); memset(d,0,sizeof(d)); queue<int>q; q.push(s); d[s]=0; vis[s]=1; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(!vis[e.to]&&e.cap>e.flow){ vis[e.to]=1; d[e.to]=d[x]+1; q.push(e.to); } } } return vis[t]; } int DFS(int x,int a){ if(x==t||a==0)return a; int flow=0,f; for(int &i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){ e.flow+=f; edges[G[x][i]^1].flow-=f; flow+=f; a-=f; if(a==0)break; } } return flow; } int Maxflow(int s,int t){ this->s=s;this->t=t; int flow=0; while(BFS()){ memset(cur,0,sizeof(cur)); flow+=DFS(s,inf); } return flow; } vector<int>Mincut(){ /// call this after maxflow vector<int>ans; for(int i=0;i<edges.size();i++){ Edge& e=edges[i]; if(vis[e.from]&&!vis[e.to]&&e.cap>0)ans.push_back(i); } return ans; } void Reduce(){ for(int i=0;i<edges.size();i++)edges[i].cap-=edges[i].flow; } }g; int main() { int n,e,c,kase=0; while(scanf("%d%d%d",&n,&e,&c)==3&&n){ g.init(n); while(e--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); g.AddEdge(a-1,b-1,c); } int flow=g.Maxflow(0,n-1); printf("Case %d: ",++kase); if(flow>=c)printf("possible\n"); else{ vector<int>cut=g.Mincut(); g.Reduce(); vector<Edge>ans; for(int i=0;i<cut.size();i++){ Edge& e=g.edges[cut[i]]; e.cap=c; g.ClearFlow(); if(flow+g.Maxflow(0,n-1)>=c)ans.push_back(e); e.cap=0; } if(ans.empty())printf("not possible\n"); else{ sort(ans.begin(),ans.end()); printf("possible option:(%d,%d)", ans[0].from+1, ans[0].to+1); for(int i = 1; i < ans.size(); i++) printf(",(%d,%d)", ans[i].from+1, ans[i].to+1); printf("\n"); } } } return 0; }
B.UVALive - 2531 (構圖最大流)
題意
有 n 個隊伍進行比賽,每個隊伍比賽數目是一樣的,每場恰好一個勝一個負,給定每個隊伍當前勝的場數敗的數目,以及兩個隊伍剩下的比賽場數,問你冠軍隊伍可能是哪些隊。
思路
對每個隊伍 i 進行判斷是不是能冠軍,最優的情況的就是剩下的比賽全都勝,也就是一共勝的數目就是剩下的要比賽的數再加上原來勝的數目sum,然後把每兩個隊伍比賽看成一個結點,(u, v),然後從 s 向 結點加一條容量要打的比賽數目的容量,然後從 (u, v) 向 u 和 v 分別加一條容量為無窮大的邊,然後每個 u 向 t 加一條容量為 sum - w[i] ,跑一個最大流,如果是滿流是,那麽就是有解,也就是 i 可能是冠軍。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
int from,to,cap,flow;
Edge(int u,int v,int c,int f)
:from(u),to(v),cap(c),flow(f){}
bool operator<(const Edge& a)const{
return from<a.from||(from==a.from&&to<a.to);
}
};
struct Dinic{
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n){
this->n=n;
for(int i=0;i<n;i++)G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int>q;
q.push(s);
d[s]=0;
vis[s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t||a==0)return a;
int flow=0,f;
for(int &i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s=s;this->t=t;
int flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(s,inf);
}
return flow;
}
}g;
const int maxt=25+5;
int n,w[maxt],d[maxt],a[maxt][maxt];
inline int ID(int u,int v){return u*n+v+1;}
inline int ID(int u){return n*n+u+1;}
bool canWin(int team){
int total=w[team];
for(int i=0;i<n;i++)total+=a[team][i];
for(int i=0;i<n;i++)
if(w[i]>total)return false;
g.init(n*n+n+2);
int full=0;
int s=0,t=n*n+n+1;
for(int u=0;u<n;u++){
for(int v=u+1;v<n;v++){
if(a[u][v]>0)g.AddEdge(s,ID(u,v),a[u][v]);
full+=a[u][v];
g.AddEdge(ID(u,v),ID(u),inf);
g.AddEdge(ID(u,v),ID(v),inf);
}
if(w[u]<total)g.AddEdge(ID(u),t,total-w[u]);
}
return g.Maxflow(s,t)==full;
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=0;i<n;i++)cin>>w[i]>>d[i];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)cin>>a[i][j];
bool first=true;
for(int i=0;i<n;i++)
if(canWin(i)){
if(first)first=false;else cout<<" ";
cout<<i+1;
}
cout<<endl;
}
return 0;
}
C.UVA - 10779 (構圖最大流)
題意
Bob與他的朋友交換貼紙;他的這些朋友只交換自己沒有的貼紙;且用的是自己所有的重復貼紙;現在要求Bob最大能得到多少張貼紙; (Bob可以不只用重復的貼紙)
思路
把人和物品都進行編號,添加原點s和匯點e,s到每個物品連邊容量為Bob擁有的數目;所有物品向匯點e連邊容量為1;
如果一個人向他擁有的物品連邊,容量為數目減1,表示他自己會留一個;如果他不擁有某件物品,則物品向這個人連一條邊,表示這個人最多接受一件這個物品;
然後跑一遍最大流就是答案了;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
struct Edge{
int from,to,cap,flow;
Edge(int u,int v,int c,int f)
:from(u),to(v),cap(c),flow(f){}
bool operator<(const Edge& a)const{
return from<a.from||(from==a.from&&to<a.to);
}
};
struct Dinic{
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n){
this->n=n;
for(int i=0;i<n;i++)G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS(){
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
queue<int>q;
q.push(s);
d[s]=0;
vis[s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t||a==0)return a;
int flow=0,f;
for(int &i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s=s;this->t=t;
int flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(s,inf);
}
return flow;
}
}g;
const int maxt=30+5;
int n,w[maxt],d[maxt],a[maxt][maxt],m;
inline int ID(int u){return u;}///物品
inline int PID(int u){return m+u;}
int S,T;
void build(){
for(int i=1;i<=m;i++){
if(a[1][i])
g.AddEdge(S,i,a[1][i]);
}
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]>=2){
g.AddEdge(PID(i),ID(j),a[i][j]-1);
}
else if(!a[i][j]){
g.AddEdge(ID(j),PID(i),1);
}
}
}
for(int i=1;i<=m;i++){
g.AddEdge(ID(i),T,1);
}
}
int main()
{
int t;
int cast=1;
cin>>t;
while(t--){
cin>>n>>m;
g.init(n+m+2);
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++){
int tot;cin>>tot;
while(tot--){
int aa;
cin>>aa;
a[i][aa]++;
}
}
S=0,T=n+m+1;
build();
cout<<"Case #"<<cast++<<": ";
cout<<g.Maxflow(S,T)<<endl;
}
return 0;
}
D.UVA - 11613 (最大費用流)
題意
A公司生產一種元素,給出該元素在未來M個月中每個月的單位售價,最大生產量,生產成本,最大銷售量和最大存儲時間,和每月存儲代價,問這家公司在M個月內所能賺大的最大利潤
思路
建邊的時候,費用我用的是相反數,所以得到最小費用後要去相反數
MCMF的時候,用一個數組紀錄了到達匯點時所花費的最小價值,因為取的是相反數,所以當價值為正時,就表示已經虧本了,所以可以退出了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=700+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
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;
vector<Edge>edges;
vector<int>G[maxn];
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init(int n){
this->n=n;
for(int i=0;i<n;i++)G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap,int cost){
edges.emplace_back(from,to,cap,0,cost);
edges.emplace_back(to,from,0,0,-cost);
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s,int t,int& flow,ll& cost){
for(int i=0;i<n;i++)d[i]=inf;
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=edges[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]>0)return false;
flow+=a[t];
cost+=(ll)d[t]*(ll)a[t];
for(int u=t;u!=s;u=edges[p[u]].from){
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
}
return true;
}
int MincostMaxflow(int s,int t,ll& cost){
int flow=0;
cost=0;
while(BellmanFord(s,t,flow,cost));
return flow;
}
}g;
int main()
{
int t;
int cast=1;
cin>>t;
int month,st_cost;
while(t--){
cin>>month>>st_cost;
g.init(month*2+2);
int source=0,sink=2*month+1;
for(int i=1;i<=month;i++){
int make_cost,make_limit,price,sell_limit,max_store;
cin>>make_cost>>make_limit>>price>>sell_limit>>max_store;
g.AddEdge(source,i,make_limit,make_cost);
g.AddEdge(month+i,sink,sell_limit,-price);
for(int j=0;j<=max_store;j++){
if(i+j<=month)
g.AddEdge(i,month+i+j,inf,st_cost*j);
}
}
ll cost=0;g.MincostMaxflow(source,sink,cost);
cout<<"Case "<<cast++<<": "<<-cost<<endl;
}
return 0;
}
訓練指南 網絡流題集