#include
using namespace std;
const int N=1e3+107;
const int inf=0x3f3f3f3f;
struct node{
int to;//终点
int cap;//容量
int rev;//反向边
};
vector v[N];
bool used[N];
void add(int from,int to,int cap)
{
v[from].push_back((node) {to,cap ,v[to].size() });
v[to].push_back((node) {from,0 ,v[from].size()-1});
}
int dfs(int s,int t,int f)
{
if(s==t) return f;
used[s]=true;
for(int i=0;i0)
{
int d=dfs(tmp.to,t,min(f,tmp.cap));
if(d>0)
{
// tmp.cap-=d;
v[s][i].cap-=d;
v[tmp.to][tmp.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t)
{
int flow=0;
while(1)
{
memset(used,false,sizeof(used));
int f=dfs(s,t,inf);
if(f==0) return flow;
flow+=f;
}
}
int main()
{
int n,m,i,j,k,t;
while(~scanf("%d%d",&n,&m))
{
memset(v,0,sizeof(v));
for(i=0;i
Dinic 算法
Edmonds-Karp的提高余地:需要多次从s到t调 用BFS,可以设法减少调用次数。
亦即:使用一种代价较小的高效增广方法。
考虑:在一次增广的过程中,寻找多条增广路 径。
DFS
Dinic算法的实现有以下几个步骤:
1:初始化容量网络和网络流
2:构造残留网络和层次网络,若汇点不在层次网络中则算法结束 输出最大流
3:在层次网络中用一次DFS进行增广,DFS执行完毕,该阶段的增广也就完毕了。
4:转至步骤2
#include
using namespace std;
const int N=205;
const int M=100010;
const int inf=0x3f3f3f3f;
int n,m,si,ei,ci;
int level[N];
struct node{
int c,f;
}mp[N][N];
bool dinic_bfs()
{
queue q;
memset(level,0,sizeof level);
q.push(1);
level[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int v=1;v<=m;v++)
if(!level[v]&&mp[u][v].c>mp[u][v].f)
{
level[v]=level[u]+1;
q.push(v);
}
}
return level[m]!=0;
}
int dinic_dfs(int u,int cp)
{
int tmp=cp;
if(u==m) return cp;
for(int v=1;v<=m&&tmp;v++)
if(level[u]+1==level[v])
{
if(mp[u][v].c>mp[u][v].f)
{
int t=dinic_dfs(v,min(tmp,mp[u][v].c-mp[u][v].f));
mp[u][v].f+=t;
mp[v][u].f-=t;
tmp-=t;
}
}
return cp-tmp;
}
int dinic()
{
int sum=0,tf=0;
while(dinic_bfs())
{
while(tf=dinic_dfs(1,inf))
{
sum+=tf;
}
}
return sum;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(mp,0,sizeof mp);
while(n--)
{
scanf("%d%d%d",&si,&ei,&ci);
mp[si][ei].c+=ci;
}
printf("%d\n",dinic());
}
return 0;
最小费用最大流
1.求出从发点到收点的最小费用路u(s,t)
2.对该通路u(s,t)分配最大可能 的流量:f
并让通路上所有边的容量相应减少f。这时,对于通路上的饱和边,其单位流费用相应改为 +无穷
3.作该通路u(s,t)上所有边(i,j)的反向边(j,i),令
4.在这样构成 的新网络中,重复上述步骤1,2,3,到从发点到收点全部流量等于f为止(或者再也找不到从S到T 的最小费用通路)。
反复用spfa算法做源到汇的最短路进行增广,边 权值为边上单位费用。反向边上的单位费用是负 的。
直到无法增广,即为找到最小费用最大流。
成立原因:每次增广时,每增加1个流量,所增 加的费用都是最小的。
因为有负权边(取消流的时候产生的),所以不 能用迪杰斯特拉算法求最短路。
http://poj.org/problem?id=2135
//#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1050;
const int M=40050;
const int inf=0x3f3f3f3f;
struct node{
int from,to,flow,cap,cost;
node(){}
node(int a,int b,int c,int d,int e)
{
from=a;to=b;flow=c;cap=d;cost=e;
}
};
vector g[N];
vector mp;
void add(int a,int b,int c,int d)
{
mp.push_back(node(a,b,0,c,d));
mp.push_back(node(b,a,0,0,-d));
int total=mp.size();
g[a].push_back(total-2);
g[b].push_back(total-1);
}
int dis[N],P[N],A[N];
bool used[N];
int m,n,flow,cost;
bool spfa(int s,int t)
{
memset(dis,inf,sizeof dis);
memset(used,0,sizeof used);
dis[s]=0;used[s]=1;P[s]=0;A[s]=inf;
queue q;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
used[u]=0;
for(int i=0;itmp.flow&&dis[tmp.to]>dis[u]+tmp.cost)
{
dis[tmp.to]=dis[u]+tmp.cost;
P[tmp.to]=g[u][i];
A[tmp.to]=min(A[u],tmp.cap-tmp.flow);
if(!used[tmp.to]) q.push(tmp.to),used[tmp.to]=1;
}
}
}
if(dis[t]==inf) return 0;
flow+=A[t];
cost+=dis[t]*A[t];
int u=t;
while(u!=s){
mp[P[u]].flow+=A[t];
mp[P[u]^1].flow-=A[t];
u=mp[P[u]].from;
}
return 1;
}
int mincost(int s,int t)
{
flow=cost=0;
while(spfa(s,t));
return cost;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(g,0,sizeof g);
mp.clear();
int s=0,t=n+1,u,v,c;
while(m--)
{
scanf("%d%d%d",&u,&v,&c);
add(u,v,1,c);
add(v,u,1,c);
}
add(s,1,2,0);
add(n,t,2,0);
printf("%d\n",mincost(s,t));
}
return 0;
}
有上下界的网络流
1、无源汇有上下界最大流
解题思路:O(-1)。
建图模型: 以前写的最大流默认的下界为0,而这里的下界却不为0,所以我们要进行再构造让每条边的下界为0,这样做是为了方便处理。对于每根管子有一个上界容量up和一个下界容量low,我们让这根管子的容量下界变为0,上界为up-low。可是这样做了的话流量就不守恒了,为了再次满足流量守恒,即每个节点"入流=出流”,我们增设一个超级源点st和一个超级终点sd。我们开设一个数组du[]来记录每个节点的流量情况。
du[i]=in[i](i节点所有入流下界之和)-out[i](i节点所有出流下界之和)。
当du[i]大于0的时候,st到i连一条流量为du[i]的边。
当du[i]小于0的时候,i到sd连一条流量为-du[i]的边。
最后对(st,sd)求一次最大流即可,当所有附加边全部满流时(即maxflow==所有du[]>0之和),有可行解。
题目链接: sgu194 Reactor Cooling
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000+10;
const int INF=0x7FFFFFFF;
struct Edge
{
int from,to,cap,flow,up,low;
};
vectoredges;
vectorG[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
int Out[maxn],In[maxn];
int s,t;
//求出层次网络
bool BFS()
{
memset(vis,0,sizeof(vis));
queueQ;
Q.push(s);
d[s]=0;
vis[s]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for(int i=0; ie.flow)
{
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
//加边
void AddEdge(int from,int to,int low,int up)
{
int m;
Edge r;
r.from=from;
r.to=to;
r.cap=up-low;
r.flow=0;
r.low=low;
r.up=up;
edges.push_back(r);
Edge d;
d.from=to;
d.to=from;
d.cap=0;
d.flow=0;
//r.low=low;r.up=up;
edges.push_back(d);
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
//每个阶段来一次DFS增广
int DFS(int x,int a)
{
if(x==t||a==0) return a;
int flow=0,f;
for(int i=cur[x]; i0)
{
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
//多个阶段,多次建立层次网络。
int Maxflow(int ss,int tt)
{
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(ss,INF);
}
return flow;
}
int main()
{
int N,M,i,j;
while(~scanf("%d%d",&N,&M))
{
edges.clear();
for(int i=0; i
2、有源汇有上下界的最大流
解题思路:增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为oo的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。
建图模型:源点s,终点d。超级源点ss,超级终点dd。首先判断是否存在满足所有边上下界的可行流,方法可以转化成无源汇有上下界的可行流问题。怎么转换呢?
增设一条从d到s没有下界容量为无穷的边,那么原图就变成了一个无源汇的循环流图。接下来的事情一样,超级源点ss连i(du[i]>0),i连超级汇点(du[i]<0),
对(ss,dd)进行一次最大流,当maxflow等于所有(du[]>0)之和时,有可行流,否则没有。
当有可行流时,删除超级源点ss和超级终点dd,再对(s,d)进行一次最大流,此时得到的maxflow则为题目的解。为什么呢?因为第一次maxflow()只是求得所有满足下界的流量,而残留网络(s,d)路上还有许多自由流(没有和超级源点和超级汇点连接的边)没有流满,所有最终得到的maxflow=(第一次流满下界的流+第二次能流通的自由流)。
题目: http://poj.org/problem?id=2396
讲解:http://blog.csdn.net/water_glass/article/details/6823741
- #include
- #include
- #include
- using namespace std;
- #define maxM 50000
- #define maxN 500
- #define inf 1<<30
- struct node{
- int u,v,f,next;
- }edge[maxM];
- int head[maxN],p,lev[maxN],cur[maxN];
- int que[maxM],tre[maxN],up[maxN][50],low[maxN][50];
- void init1(int n,int m){
- p=0,memset(head,-1,sizeof(head)),memset(tre,0,sizeof(tre));
- for(int i=0;i<=n;i++) for(int j=0;j<=m;j++)
- up[i][j]=inf,low[i][j]=0;
- }
- bool bfs(int s,int t){
- int qin=0,qout=0,u,i,v;
- memset(lev,0,sizeof(lev));
- lev[s]=1,que[qin++]=s;
- while(qout!=qin){
- u=que[qout++];
- for(i=head[u];i!=-1;i=edge[i].next){
- if(edge[i].f>0 && lev[v=edge[i].v]==0){
- lev[v]=lev[u]+1,que[qin++]=v;
- if(v==t) return 1;
- }
- }
- }
- return lev[t];
- }
- int dinic(int s,int t){
- int qin,u,i,k,f;
- int flow=0;
- while(bfs(s,t)){
- memcpy(cur,head,sizeof(head));
- u=s,qin=0;
- while(1){
- if(u==t){
- for(k=0,f=inf;k
- if(edge[que[k]].f
- f=edge[que[i=k]].f;
- for(k=0;k
- edge[que[k]].f-=f,edge[que[k]^1].f+=f;
- flow+=f,u=edge[que[qin=i]].u;
- }
- for(i=cur[u];cur[u]!=-1;i=cur[u]=edge[cur[u]].next)
- if(edge[i].f>0 && lev[u]+1==lev[edge[i].v]) break;
- if(cur[u]!=-1)
- que[qin++]=cur[u],u=edge[cur[u]].v;
- else{
- if(qin==0) break;
- lev[u]=-1,u=edge[que[--qin]].u;
- }
- }
- }
- return flow;
- }
- void addedge(int u,int v,int f){
- edge[p].u=u,edge[p].v=v,edge[p].f=f,edge[p].next=head[u],head[u]=p++;
- edge[p].u=v,edge[p].v=u,edge[p].f=0,edge[p].next=head[v],head[v]=p++;
- }
- bool buit(int n,int m){
- for(int i=1;i<=n;i++)
- for(int j=1;j<=m;j++)
- if(low[i][j]>up[i][j]) return 0;
- else{
- tre[i]-=low[i][j],tre[j+n]+=low[i][j];
- addedge(i,j+n,up[i][j]-low[i][j]);
- }
- return 1;
- }
- void limitflow(int s,int t,int n,int m){
- int i,j,x,y;
- x=t+1,y=t+2;
- for(i=0;i<=t;i++){
- if(tre[i]>0) addedge(x,i,tre[i]);
- else if(tre[i]<0) addedge(i,y,-tre[i]);
- }
- addedge(t,s,inf);
- dinic(x,y);
- for(i=head[x];i!=-1;i=edge[i].next)
- if(edge[i].f){
- printf("IMPOSSIBLE\n\n"); return ;
- }
- for(i=head[t];i!=-1;i=edge[i].next)
- if(edge[i].v==s) break;
- if(i<0){
- printf("IMPOSSIBLE\n\n"); return;
- }
- for(i=1;i<=n;i++){
- for(j=1;j
- printf("%d ",edge[((i-1)*m+j)*2-1].f+low[i][j]);
- printf("%d\n",edge[i*m*2-1].f+low[i][j]);
- }
- printf("\n");
- }
- int main(){
- int cas,cas1,n,m,i,j,sum1,sum2;
- int u,v,d,f1,t1,f2,t2,s,t;
- char c[5];
- scanf("%d",&cas);
- for(int tt=1;tt<=cas;tt++){
- scanf("%d%d",&n,&m);
- s=0,t=n+m+1,sum1=0,sum2=0;
- init1(n,m);
- for(i=1;i<=n;i++) scanf("%d",&u),tre[s]-=u,tre[i]+=u,sum1+=u;;
- for(i=n+1;i<=n+m;i++) scanf("%d",&u),tre[i]-=u,tre[t]+=u,sum2+=u;
- scanf("%d",&cas1);
- while(cas1--){
- scanf("%d%d%s%d",&u,&v,c,&d);
- f1=t1=u,f2=t2=v;
- if(u==0) f1=1,t1=n;
- if(v==0) f2=1,t2=m;
- for(i=f1;i<=t1;i++)
- for(j=f2;j<=t2;j++){
- if(c[0]=='='){
- low[i][j]=max(d,low[i][j]),up[i][j]=min(d,up[i][j]);
- }else if(c[0]=='>'){
- low[i][j]=max(d+1,low[i][j]);
- }else if(c[0]=='<'){
- up[i][j]=min(d-1,up[i][j]);
- }
- }
- }
- if(sum1==sum2 && buit(n,m)) limitflow(s,t,n,m);
- else printf("IMPOSSIBLE\n\n");
- }
- return 0;
- }
3、有源汇有上下界的最小流
解题思路:
1、du[i]表示i节点的入流之和与出流之和的差。
2、增设超级源点st和超级汇点sd,连(st,du[i](为正)),(-du[i](为负),sd)。 ///增设超级源点和超级汇点,因为网络中规定不能有弧指向st,也不能有流量流出sd
3、做一次maxflow()。
4、源点(Sd)和起点(St)连一条容量为oo的边。
5、再做一次maxflow()。
6、当且仅当所有附加弧满载时有可行流,最后答案为flow[(Sd->St)^1],St到Sd最大流就是Sd到St最小流。
http://acm.hdu.edu.cn/showproblem.php?pid=3157
- /*
- hdu 3157
- poj 3801
- 题意:一个电路板,上面有N个接线柱(标号1~N) 还有两个电源接线柱 + -
- 然后是 给出M个部件正负极的接线柱和最小电流
- 求一个可以让所有部件正常工作的总电流 没有则输出impossible
-
- 其实就是一个 有源汇 有上下界 最小流 问题
-
- 处理有源汇有上下界最大流问题是:
- 1.构造附加网络
- 2.对ss、tt求最大流(ss、tt满流则有解)
- 3.若有解,对s、t求最大流
-
- 而有源汇有上下界最小流问题则是:
- 1.构造附加网络(不添加[t,s]边)
- 2.对ss、tt求最大流
- 3.添加[t,s]边
- 4.对ss、tt求最大流
- 5.若ss、tt满流,则[t,s]的流量就是最小流
-
- 这个代码大部分都是从别的题上摘过来的,注释有点乱
- */
- #include
- #include
- #define inf 0x7fffffff
- struct edge//边
- {
- int u,v,f,next,b,c;//边的 前节点 后节点 可用流 下条边的编号 原来边上流的上下界
- }e[1500];
- int head[70],in[70],s,t,ss,tt,yong,sum;
- int n,m;
- void ini()
- {
- memset(head,-1,sizeof(head));
- yong=0;
- memset(in,0,sizeof(in));
- s=0,t=n+1,ss=t+1,tt=ss+1;//各节点编号的安排
- sum=0;
- }
- void adde(int from,int to,int xia,int shang)//加边
- { //加边
- e[yong].u=from,e[yong].v=to,e[yong].f=shang-xia,e[yong].b=xia,e[yong].c=shang;
- e[yong].next=head[from],head[from]=yong++;
- //同时加它的退边
- e[yong].u=to,e[yong].v=from,e[yong].f=0,e[yong].b=xia,e[yong].c=shang;
- e[yong].next=head[to],head[to]=yong++;
- }
- void build()
- {
- int i;
- for(i=0;i<=t;++i)
- {
- if(in[i]>0)
- adde(ss,i,0,in[i]);
- else
- {
- adde(i,tt,0,-in[i]);
- sum+=(-in[i]);
- }
- }
- }
- int d[1000],num[1000];
- int min(int a,int b){return a
- int sap_gap(int u,int f,int s,int t)//递归sap
- {
- if(u==t)
- return f;
- int i,v,mind=t,last=f,cost;
- for(i=head[u];i!=-1;i=e[i].next)
- {
- v=e[i].v;
- int flow=e[i].f;
- if(flow>0)//参考模版写的时候把flow写成了f
- {
- if(d[u]==d[v]+1)
- {
- cost=sap_gap(v,min(last,flow),s,t);
- e[i].f-=cost;
- e[i^1].f+=cost;
- last-=cost;
-
- if(d[s]>=t+1)
- return f-last;
-
- if(last==0)
- break;
- }
- if(d[v]
- mind=d[v];
- }
- }
-
- if(last==f)
- {
- --num[d[u]];
- if(num[d[u]]==0)
- d[s]=t+1;
- d[u]=mind+1;
- ++num[d[u]];
- }
- return f-last;
- }
- int max_f(int s,int t)
- {
- int f=0;
- memset(d,0,sizeof(d));
- memset(num,0,sizeof(num));
- for(num[s]=t+1;d[s]
- f+=sap_gap(s,inf,s,t);
- return f;
- }
- int main()
- {
- int i,dat,u,v,f1,f2,p;
- char from[10],to[10];
- while(scanf("%d%d",&n,&m),n+m)
- {
- ini();
- for(i=1;i<=m;++i)
- {
- scanf("%s%s%d",from,to,&dat);
- if(from[0]=='+') u=s;
- else sscanf(from,"%d",&u);
- if(to[0]=='-') v=t;
- else sscanf(to,"%d",&v);
- adde(u,v,dat,inf);
- in[u]-=dat,in[v]+=dat;
- }
- build();
-
- f1=max_f(ss,tt);
- p=yong;
- adde(t,s,0,inf);
- f2=max_f(ss,tt);
- if(f1+f2!=sum) printf("impossible\n");
- else printf("%d\n",e[p^1].f);
- }
- return 0;
- }
你可能感兴趣的:(ACM-ICPC,2016暑期特训1)