网络流

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> <script type="math/tex" id="MathJax-Element-7"> 属于原图</script>
最大权闭合图 wmax = wu>0wuMaxflow(S,T)

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

3.DAG图的最小点路径覆盖
最少几条点不相交的路径可以覆盖DAG图
拆点,add(i,j+n,1), <i,j> <script type="math/tex" id="MathJax-Element-10"> </script>属于E,求最大二分图匹配
可以用匈牙利算法或者最大流做。
最大流在构造可行解时,需要从大于等于n+1的节点堆中找到第一个起点,即 k+n>2n+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;
            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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值