您的位置:首页 > 理论基础 > 计算机网络

网络流

2015-08-22 23:05 701 查看
1.飞行员配对方案

有权二分图最大匹配,KM算法解决 O(n^3)

2.太空飞行计划

选Bi必要条件/前提是选择集合{Aj},每个点有一个权值,求满足依赖关系的最大点权集合。

建图方式为Bi向{Aj}中的每一个点都连一条边,转化为找该图中的最大权闭合图。

闭合图定义:闭合图中边的终点一定在闭合图中(无出边),恰好满足题目的必要条件要求(必要条件属于该集合)

求法:建立网络图:

add(S,u,w) wu>0

add(u,T,-w) wu<0

add(u,v,INF) <u,v>属于原图

最大权闭合图wmax=∑wu>0wu−Maxflow(S,T)

拓展:

1.如果依赖关系存在环,那么可以先对原图缩点,产生新的DAG图,缩点的权值为点内权值和。在新图中采用上述方法求解。

3.DAG图的最小点路径覆盖

最少几条点不相交的路径可以覆盖DAG图

拆点,add(i,j+n,1),<i,j>属于E,求最大二分图匹配

可以用匈牙利算法或者最大流做。

最大流在构造可行解时,需要从大于等于n+1的节点堆中找到第一个起点,即k+n−>2∗n+1的残量不为0。然后再以k为起点遍历寻找路径,寻找的过程是找残量为0的边

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<string.h>
#include<math.h>
#include<queue>
#include<map>
#define N maxn
#define ll long long
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 6000+10;
int m,n;
struct node{
int nx,v,cap,flow;
}e[maxn<<2];
int hd[maxn],tt,S,T;

void init(){
memset(hd,-1,sizeof(hd)); tt=0;
S=0,T=2*n+1;
}

void add(int u,int v,int cap){
e[tt].v=v,e[tt].nx=hd[u],e[tt].cap=cap,e[tt].flow=0,hd[u]=tt++;
}

void addedge(int u,int v,int cap){
add(u,v,cap),add(v,u,0);
}

int d[maxn],used[maxn];

bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
q.push(S),d[S]=0;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=hd[u];i!=-1;i=e[i].nx){
int v=e[i].v;

1bddf
if(d[v]==-1&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1; q.push(v);
}
}
}
return d[T]!=-1;
}

int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;used[u]=i=e[i].nx){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(a==0) break;
}
}
return f;
}

int Maxflow(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=hd[i];
ans+=DFS(S,INF);
}
return ans;
}

void dfs(int u){
for(int i=hd[u];i!=-1;i=e[i].nx){
int v=e[i].v;
if(e[i].cap==e[i].flow&&v>=n+1&&v<=2*n){//找残量为0的边
printf(" %d",v-n);
dfs(v-n);
}
}
}

int main(){
//freopen("path3.in","r",stdin);
//freopen("path3.out","w",stdout);
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++) addedge(S,i,1),addedge(i+n,T,1);
for(int i=1;i<=m;i++){
int x,y; scanf("%d%d",&x,&y);
addedge(x,y+n,1);
}
int f=Maxflow(S,T);
for(int i=1+n;i<=2*n;i++){
for(int j=hd[i];j!=-1;j=e[j].nx){
int v=e[j].v;
if(v==T&&e[j].flow<e[j].cap){//找到路径的所有起点
printf("%d",i-n); dfs(i-n); printf("\n");
}
}
}
printf("%d\n",n-f);
return 0;
}


hdu4780

题意:给定n个时间区间加工n种方案,共有m台机器进行加工,同一台机器相同时间只加工一种方案。

1.如果某方案的起始时间大于规定时间,那么需要付出额外代价。

2.同一台机器对于加工的第一个方案有一个延迟开始时间和额外代价。

3.不同方案之间转换也有一个延迟时间和额外代价

求加工所有方案所付出的最小额外代价。

解法:经过分析可得:

1.每个方案加工结束的时间固定不变

2.只要确定每个方案前的加工方案,就可以确定该方案需要付出的额外代价

因此为了确定总额外代价,只需要找到每个方案的前驱即可

并且题意满足:如果i方案能够在j方案后进行加工,那么j方案一定不可以在i方案前进行加工!

建图如下:

二分图左侧有n+m个点,表示待定的前驱,右侧有n个点,表示需要确定前驱的点。如果i方案后可以加工j方案/i机器可以首先加工j方案,那么从左向右引边即可。最后建立一个超级源点和超级汇点,求最小费用最大流即可。

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1000;
const int maxm = 4000000+10;
int n,m,k;
int candy_s[maxn],candy_t[maxn];
int c[maxn][maxn],d[maxn][maxn];
int E[maxn][maxn],f[maxn][maxn];
int S,T,ct,mf;
struct node{
int u,v,cap,flow,cost,nxt;
}e[maxm];
int head[maxn],tot;

void read(int a[][maxn],int n,int m){
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
}

void add(int u,int v,int cap,int cost){
e[tot].u=u,e[tot].v=v,e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,e[tot].nxt=head[u],head[u]=tot++;
e[tot].u=v,e[tot].v=u,e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,e[tot].nxt=head[v],head[v]=tot++;
}
int p[maxn];

void spfa(int S,int T,int d[]){
bool vis[maxn];
queue<int> q;
memset(vis,0,sizeof(vis));
for(int i=S;i<=T;i++) d[i]=INF,p[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front();q.pop(); vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){
d[v]=d[u]+e[i].cost,p[v]=i;
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}

void mcmf(int S,int T){
ct=mf=0;
while(1){
int d[maxn];
spfa(S,T,d);
if(d[T]==INF) break;
int a=INF;
for(int u=p[T];u!=-1;u=p[e[u].u]) a=min(a,e[u].cap-e[u].flow);
for(int u=p[T];u!=-1;u=p[e[u].u]) e[u].flow+=a,e[u^1].flow-=a;
ct+=d[T]*a,mf+=a;
}
}

int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
if(n==0&&m==0&&k==0) break;
for(int i=1;i<=n;i++) scanf("%d%d",&candy_s[i],&candy_t[i]);
read(c,n,m),read(d,n,m),read(E,n,n),read(f,n,n);
S=0,T=2*n+m+1;
memset(head,-1,sizeof(head)); tot=0;
for(int i=1;i<=n;i++){
add(S,i,1,0);
for(int j=1;j<=n;j++){
int st=candy_t[i]+E[i][j],cost=f[i][j];
if(i==j||st>=candy_t[j]) continue;
if(st>candy_s[j]) cost+=k*(st-candy_s[j]);
add(i,n+m+j,1,cost);
}
for(int j=1;j<=m;j++){
if(c[i][j]>=candy_t[i]) continue;
int cost=d[i][j];
if(c[i][j]>candy_s[i]) cost+=k*(c[i][j]-candy_s[i]);
add(n+j,i+m+n,1,cost);
}
add(i+m+n,T,1,0);
}
for(int i=1;i<=m;i++) add(S,n+i,1,0);
mcmf(S,T);
if(mf<n) printf("-1\n");
else printf("%d\n",ct);
}
return 0;
}


uvalive 2531

题意:n支队伍进行比赛,每支队伍进行比赛数目相同,每场比赛一支队伍取胜,一支队伍败。给出每支队伍当前胜场和负场,以及每两个队伍还剩余的比赛场数,求可能获得冠军的队伍。

解法:枚举每支队伍,判断其是否可以获得冠军。判断过程:将m场比赛的胜利分配给n个队伍,每支队伍还可以获胜的次数不超过total-w[i],total为当前枚举队伍的胜场数,w[i]为第i支队伍的当前胜场数,判断是否可以将所有比赛的胜利分配出去。

确定分配关系和个数,且得到个数有上限的分配问题可以建立二分图,求最大流,如果满流,那么全都分配出去了。

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1000+10;
const int maxm = 1000000+10;
int t,n;
struct node{
int v,nxt,cap,flow;
}e[maxm];
int head[maxn],tot;
int w[maxn],def[maxn],a[maxn][maxn],b[maxn][maxn],total,tmp,S,T;
int ans[maxn],cnt;
void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++;
}

void update(int k){
total=w[k];
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){
if(i==k||j==k){
total+=a[i][j];
b[i][j]=0;
}
else b[i][j]=a[i][j];
}
}

void build(int k){
S=0,T=n+1,tmp=0;
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){
if(b[i][j]){
tmp+=b[i][j];
add(S,T,b[i][j]);
add(T,i,INF),add(T,j,INF);
T++;
}
}
for(int i=1;i<=n;i++){
if(i!=k) add(i,T,total-w[i]);
}
}

int d[maxn],used[maxn];

bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
q.push(S),d[S]=0;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1; q.push(v);
}
}
}
return d[T]!=-1;
}

int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(a==0) break;
}
}
return f;
}

int maxflow(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF);
}
return ans;
}

int main(){
//freopen("a.txt","r",stdin);
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&def[i]);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
cnt=0;
for(int k=1;k<=n;k++){
update(k);
bool flag=false;
for(int i=1;i<=n;i++) if(total<w[i]) { flag=true; break; }
if(flag) continue;
memset(head,-1,sizeof(head)); tot=0;
build(k);
int mf=maxflow(S,T);
if(mf==tmp) ans[cnt++]=k;
}
for(int i=0;i<cnt;i++){
if(i==cnt-1) printf("%d\n",ans[i]);
else printf("%d ",ans[i]);
}
}
return 0;
}


uva10779

题意:1个人和另外n个人交换m件物品,交换规则如下:

1.n个人只和他交换,不会互相交换

2.n个人只会用手上重复的物品交换他所没有的物品

问最终他最多可以获得多少种不同的物品?

解法:1-n交换m物品问题,对于每个人,能给他和从他那里得到的是两个m的不相交子集,求能够得到的最大不同物品集合

建图如下:

1-m表示m个物品,m+1-m+n表示n个人

add(S,i,a[1][i]):a[1][j]为第一个人的最初的j物品数量

如果第i个人没有j物品,那么他可以得到1件j物品,否则他可以给出num-1个j物品

最后将1-m连向T,容量为1,表示能够得到的最大不同数量

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 1000+10;
const int maxk = 100;
const int maxm = 1000000+10;
int t,n,m;
int a[maxn][maxn];
int S,T;
struct node{
int v,nxt,cap,flow;
}e[maxm];
int head[maxn],tot;
int change[maxk][maxk];
void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++;
}

int d[maxn],used[maxn];

bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1; q.push(v);
}
}
}
return d[T]!=-1;
}

int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(!a) break;
}
}
return f;
}

int maxflow(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF);
}
return ans;
}

int main(){
//freopen("a.txt","r",stdin);
scanf("%d",&t);
int cas=0;
while(t--){
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++){
int num,x; scanf("%d",&num);
while(num--){
scanf("%d",&x);
a[i][x]++;
}
}
S=0,T=m+n;
memset(head,-1,sizeof(head));tot=0;
for(int i=1;i<=m;i++){
add(S,i,a[1][i]);
for(int j=2;j<=n;j++){
if(!a[j][i]) add(i,j+m-1,1);
else if(a[j][i]>1) add(j+m-1,i,a[j][i]-1);
}
add(i,T,1);
}
printf("Case #%d: %d\n",++cas,maxflow(S,T));
}
return 0;
}


uva11613

题意:m个月每个月最多可以生产ni件物品,每件生产费用为mi,可以储存ei个月,最多可以卖出si件物品,单价为pi。每件物品储存一个月消耗成本I,求最大利润。

解法:将M个月拆成2*M个点,分别表示买进和卖出,建立二分图+S+T,求解最小费用最大流即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF=0x3FFFFFFFFFFFFFFFLL;
const ll maxn = 200+10;
const ll maxm = 10000+10;
ll t;
ll S,T;
ll M,I,m,n,P,s,E;
struct node{
ll u,v,nxt,cap,flow,cost;
}e[maxm<<2];
ll head[maxn],tot,p[maxn];

void add(ll u,ll v,ll cap,ll cost){
e[tot].u=u,e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,head[u]=tot++;
e[tot].u=v,e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,head[v]=tot++;
}

void spfa(ll S,ll T,ll d[]){
bool vis[maxn];
queue<int> q;
memset(vis,0,sizeof(vis));
for(ll i=S;i<=T;i++) d[i]=INF,p[i]=-1;
d[S]=0; q.push(S);
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;
for(ll i=head[u];i!=-1;i=e[i].nxt){
ll v=e[i].v;
if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){
d[v]=d[u]+e[i].cost,p[v]=i;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
}

ll mcmf(ll S,ll T){
ll mf=0,mc=0;
while(1){
ll d[maxn];
spfa(S,T,d);
if(d[T]==INF) break;
if(d[T]>=0) break;
ll a=INF;
for(ll i=p[T];i!=-1;i=p[e[i].u]) a=min(a,e[i].cap-e[i].flow);
for(ll i=p[T];i!=-1;i=p[e[i].u]) e[i].flow+=a,e[i^1].flow-=a;
mf+=a,mc+=a*d[T];
}
return mc;
}

int main(){
//freopen("a.txt","r",stdin);
scanf("%lld",&t);
ll cas=0;
while(t--){
scanf("%lld%lld",&M,&I);
S=0,T=2*M+1;
memset(head,-1,sizeof(head)); tot=0;
for(ll i=1;i<=M;i++){
scanf("%lld%lld%lld%lld%lld",&m,&n,&P,&s,&E);
add(S,i,n,m);
for(ll j=0;j<=E;j++){
if(j+i>M) break;
add(i,j+i+M,INF,j*I);
}
add(i+M,T,s,-P);
}
printf("Case %lld: %lld\n",++cas,-mcmf(S,T));
}
return 0;
}


uva10806

题意:从s到t再返回s不重复经过同一条边的最短路径

解法:原题可以转化为:最大流为2的最小费用流

建图:S’->S,建立流量为2,费用为0的边,求S’->T的最小费用流即可

拓展:

1.求流量为k的最小费用流:S->S’,建立流量为k,费用为0的边即可

2.从s到t再返回s不重复经过同一点(除s)的最短路径

拆点限制点经过一次,求费用为2的最小费用流即可

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 2000+10;
const int maxm = 100000+10;
int n,m;
struct node{
int u,v,nxt,cap,flow,cost;
}e[maxm<<2];
int head[maxn],tot,p[maxn];
int mc,mf;

void add(int u,int v,int cap,int cost){
e[tot].u=u,e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,head[u]=tot++;
e[tot].u=v,e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,head[v]=tot++;
}

void spfa(int S,int T,int d[]){
queue<int> q;
bool vis[maxn];
memset(vis,0,sizeof(vis));
for(int i=S;i<=T;i++) d[i]=INF,p[i]=-1;
d[S]=0; q.push(S);
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;//!!
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){
d[v]=d[u]+e[i].cost,p[v]=i;//!!
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
}

void mcmf(int S,int T){
mc=mf=0;
while(1){
int d[maxn];
spfa(S,T,d);
if(d[T]==INF) break;
int a=INF;
for(int i=p[T];i!=-1;i=p[e[i].u]) a=min(a,e[i].cap-e[i].flow);
for(int i=p[T];i!=-1;i=p[e[i].u]) e[i].flow+=a,e[i^1].flow-=a;
mf+=a,mc+=d[T]*a;
}
}

int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
if(!n) break;
scanf("%d",&m);
memset(head,-1,sizeof(head)); tot=0;
add(0,1,2,0);
while(m--){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,1,z); add(y,x,1,z);
}
mcmf(0,n);
if(mf>=2) printf("%d\n",mc);
else printf("Back to jail\n");
}
return 0;
}


uvalive3268

题意:n个人中每个人可以分配到某些组中,但实际上每个人只能分配到一组中去。求组中人员最多的最小值

解法:分配问题,建立二分图,二分组中元素个数即可

uva11167

题意:每个猴子在一个固定时间段[si,ti]内可以喝水,单位时间只喝一单位水,而且必须要喝掉Vi单位水,但是同一个时间点最多有m个猴子同时喝水。输出一种合理的时间分配方案。

解法:分配模型,建立二分图。但是时间取值范围较大,需要离散化。在残余网络中从S出发遍历残量不为0的边,在此段时间内取出一段合法解即可,但是需要合并连续的区间,比较麻烦。

#include<bits/stdc++.h>
using namespace std;

int n,m;
const int INF = 0x3f3f3f3f;
const int maxn = 50100+10;
const int maxm = 5000000+10;
struct node{
int v,nxt,cap,flow;
}e[maxm<<1];
int head[maxn],tot,vis[maxn];
int S,T,sum;
int l[maxn],r[maxn],v[maxn],a[maxn],b[maxn],tim[maxn],id[maxn],cnt;
int now[maxn];
struct Tim{
int st,ed;
}ans[maxn],tans[maxn];

bool cmp(Tim a,Tim b){
return a.st<b.st;
}

void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++;
}

int used[maxn],d[maxn];

bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap>e[i].flow){
d[v]=d[u]+1; q.push(v);
}
}
}
return d[T]!=-1;
}

int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(a==0) break;
}
}
return f;
}

int mf(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF);
}
return ans;
}

void solve(){
for(int u=1;u<=n;u++){//记录合法区间
int cnt2=0,cnt3=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v,f=e[i].flow;
if(e[i].flow<=0) continue;
if(now[v]+f>tim[v-n-1+1]){
int tmp=tim[v-n-1+1]-now[v];
if(tmp) ans[cnt2].st=now[v],ans[cnt2++].ed=tim[v-n-1+1];
ans[cnt2].st=tim[v-n-1],ans[cnt2++].ed=tim[v-n-1]+f-tmp;
now[v]=tim[v-n-1]+f-tmp;
}
else{
ans[cnt2].st=now[v],ans[cnt2++].ed=now[v]+f;
now[v]+=f;
}
}
sort(ans,ans+cnt2,cmp);
for(int i=0;i<cnt2;){// 合并区间
int j=i;
while(j+1<cnt2&&ans[j+1].st==ans[j].ed) j++;
tans[cnt3].st=ans[i].st,tans[cnt3++].ed=ans[j].ed;
i=j+1;
}
printf("%d",cnt3);
for(int i=0;i<cnt3;i++)
printf(" (%d,%d)",tans[i].st,tans[i].ed);
printf("\n");
}
}

int main(){
int cas=0;
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
if(!n) break;
scanf("%d",&m);
S=0;
memset(head,-1,sizeof(head)); tot=0,cnt=0,sum=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v[i],&a[i],&b[i]);
sum+=v[i];
if(!vis[a[i]]) tim[cnt++]=a[i],vis[a[i]]=1;
if(!vis[b[i]]) tim[cnt++]=b[i],vis[b[i]]=1;
}
sort(tim,tim+cnt);
T=n+cnt+2;
for(int i=0;i<cnt;i++){
id[tim[i]]=i;
now[i+n+1]=tim[i];
if(i!=cnt-1) add(i+n+1,T,m*(tim[i+1]-tim[i]));
}
for(int i=1;i<=n;i++){
int l=id[a[i]],r=id[b[i]];
//printf("%d %d\n",l,r);
add(S,i,v[i]);
for(int j=l;j<=r-1;j++){
//printf("%d %d\n",i,j+n+1);
add(i,j+n+1,tim[j+1]-tim[j]);
}
}
int tmp=mf(S,T);
//printf("%d %d\n",tmp,sum);
if(tmp==sum) { printf("Case %d: Yes\n",++cas); solve(); }
else printf("Case %d: No\n",++cas);
}
return 0;
}


uva11082

题意:已知矩阵每行和每列的和,构造出一组可行解,矩阵中每个值的变化范围是[1,20]

解法:行与列的值分配问题,建立二分图模型。关键点在于行之和=列之和,由此可得到流量平衡,容易建图。需要避免流量为0的情况,技巧在于先把所有值-1,求出结果后再都加上1即可

#include<bits/stdc++.h>
using namespace std;

int n,m;
const int INF = 0x3f3f3f3f;
const int maxn = 50100+10;
const int maxm = 5000000+10;
struct node{
int v,nxt,cap,flow;
}e[maxm<<1];
int head[maxn],tot,vis[maxn];
int S,T,sum;
int l[maxn],r[maxn],v[maxn],a[maxn],b[maxn],tim[maxn],id[maxn],cnt;
int now[maxn];
struct Tim{
int st,ed;
}ans[maxn],tans[maxn];

bool cmp(Tim a,Tim b){
return a.st<b.st;
}

void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++;
}

int used[maxn],d[maxn];

bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap>e[i].flow){
d[v]=d[u]+1; q.push(v);
}
}
}
return d[T]!=-1;
}

int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(a==0) break;
}
}
return f;
}

int mf(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF);
}
return ans;
}

void solve(){
for(int u=1;u<=n;u++){
int cnt2=0,cnt3=0;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v,f=e[i].flow;
if(e[i].flow<=0) continue;
if(now[v]+f>tim[v-n-1+1]){
int tmp=tim[v-n-1+1]-now[v];
if(tmp) ans[cnt2].st=now[v],ans[cnt2++].ed=tim[v-n-1+1];
ans[cnt2].st=tim[v-n-1],ans[cnt2++].ed=tim[v-n-1]+f-tmp;
now[v]=tim[v-n-1]+f-tmp;
}
else{
ans[cnt2].st=now[v],ans[cnt2++].ed=now[v]+f;
now[v]+=f;
}
}
sort(ans,ans+cnt2,cmp);
for(int i=0;i<cnt2;){
int j=i;
while(j+1<cnt2&&ans[j+1].st==ans[j].ed) j++;
tans[cnt3].st=ans[i].st,tans[cnt3++].ed=ans[j].ed;
i=j+1;
}
printf("%d",cnt3);
for(int i=0;i<cnt3;i++)
printf(" (%d,%d)",tans[i].st,tans[i].ed);
printf("\n");
}
}

int main(){
int cas=0;
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
if(!n) break;
scanf("%d",&m);
S=0;
memset(head,-1,sizeof(head)); tot=0,cnt=0,sum=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v[i],&a[i],&b[i]);
sum+=v[i];
if(!vis[a[i]]) tim[cnt++]=a[i],vis[a[i]]=1;
if(!vis[b[i]]) tim[cnt++]=b[i],vis[b[i]]=1;
}
sort(tim,tim+cnt);
T=n+cnt+2;
for(int i=0;i<cnt;i++){
id[tim[i]]=i;
now[i+n+1]=tim[i];
if(i!=cnt-1) add(i+n+1,T,m*(tim[i+1]-tim[i]));
}
for(int i=1;i<=n;i++){
int l=id[a[i]],r=id[b[i]];
add(S,i,v[i]);
for(int j=l;j<=r-1;j++){
add(i,j+n+1,tim[j+1]-tim[j]);
}
}
int tmp=mf(S,T);
if(tmp==sum) { printf("Case %d: Yes\n",++cas); solve(); }
else printf("Case %d: No\n",++cas);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: