zoukankan      html  css  js  c++  java
  • 算法竞赛入门经典 写题笔记(第五章 图论算法与模型4)

    本节内容——

    • 网络流问题及常见模型

    例题27 我是SAM

    说是SAM但是并没有什么关系
    给出一个R*C大小的网络,网格上面放了一些目标。可以在网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标。你的任务是计算出最少需要多少子弹,各从哪些位置发射,才能把所有目标全部打掉。(还要输出任意一种合法方案)

    就是一个二分图最小覆盖问题,把X坐标(一行)和Y坐标(一列)划分成二分图,如果有一个目标位置为(x,y),那么连行X和列Y。
    然后因为二分图最小点覆盖即最小割,我们直接上dinic即可。
    麻烦的是如何输出任意一组最小割方案——
    (如果只输出左右端点,不考虑中间连边——即INF时)跑一遍dinic,然后在残量网络上记录done数组,最后一遍bfs左边没有访问到的点和右边访问到的点就是最小割。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define S 0
    #define T n+m+1
    #define MAXN 100010
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,t,k,top,tot,cnt;
    int head[MAXN],cur[MAXN],dis[MAXN],done[MAXN];
    int dfn[MAXN],low[MAXN],c[MAXN],st[MAXN],in[MAXN];
    struct Edge{int nxt,to,dis;}edge[MAXN<<1];
    struct Node{char op;int pos;};
    vector<Node>vec;
    inline void add(int from,int to,int dis)
    {
        edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
    }
    inline bool bfs()
    {
        queue<int>q;
        memset(dis,0x3f,sizeof(dis));
        memcpy(cur,head,sizeof(head));
        memset(done,0,sizeof(done));
        q.push(S);dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(edge[i].dis&&dis[v]==0x3f3f3f3f)
                    dis[v]=dis[u]+1,q.push(v),done[v]=1;
            }
        }
        if(dis[T]==0x3f3f3f3f) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(!f||x==T) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            int v=edge[i].to;
            if(dis[v]==dis[x]+1&&(w=dfs(v,min(f,edge[i].dis))))
            {
                edge[i].dis-=w,edge[i^1].dis+=w;
                f-=w,used+=w;
                if(!f) break;
            }
        }
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) cur_ans+=dfs(S,(int)1e9);
        return cur_ans;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        while(scanf("%d%d%d",&n,&m,&k)==3)
        {
            if(n==0&&m==0&&k==0) break;
            vec.clear();
            memset(head,0,sizeof(head));
            t=1,top=tot=cnt=0;
            for(int i=1;i<=k;i++)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                add(x,y+n,INF);//printf("[%d %d] %d
    ",x,y+n,INF);
            }
            for(int i=1;i<=n;i++) add(S,i,1);//printf("[%d %d] %d
    ",S,i,1);
            for(int i=1;i<=m;i++) add(i+n,T,1);//printf("[%d %d] %d
    ",i+n,T,1);
            int ans=dinic();
            printf("%d ",ans);
            for(int i=1;i<=n;i++)
                if(!done[i])
                {
                    Node cur;
                    cur.op='r',cur.pos=i;
                    vec.push_back(cur);
                }
                    // printf("r%d ",i);
            for(int i=1;i<=m;i++)
                if(done[i+n])
                {
                    Node cur;
                    cur.op='c',cur.pos=i;
                    vec.push_back(cur);
                }
                    // printf("c%d ",i);
            for(int i=0;i<vec.size()-1;i++) printf("%c%d ",vec[i].op,vec[i].pos);
            printf("%c%d
    ",vec[vec.size()-1].op,vec[vec.size()-1].pos);
        }
        return 0;
    }
    

    例题28 保守的老师

    翻译自己看去.......
    就是4个条件只要满足任意一个就要选取,也就是——4个条件全部不满足只能任选其一。那么我们考虑只要4个条件都不满足,就连一条边,然后跑二分图最大独立集就行了。(可以根据是男生还是女生分成二分图)
    二分图最大独立集等于n-最大流。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define S 0
    #define T n+1
    #define MAXN 100010
    #define INF 0x3f3f3f3f
    using namespace std;
    int tt,n,m,t,tot;
    int head[MAXN],dis[MAXN],cur[MAXN];
    struct Edge{int nxt,to,dis;}edge[MAXN<<1];
    struct Node{int a,id;char b;string c,d;}node[MAXN];
    inline void add(int from,int to,int dis)
    {
        // if(from!=S&&to!=T) printf("[%d %d]
    ",from,to);
        edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
    }
    inline bool bfs()
    {
        memset(dis,0x3f,sizeof(dis));
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(S);
        dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]==0x3f3f3f3f&&edge[i].dis)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[T]==0x3f3f3f3f) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(x==T||!f) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            if(dis[edge[i].to]==dis[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
            {
                used+=w,f-=w;
                edge[i].dis-=w,edge[i^1].dis+=w;
                if(!f) break;
            }
        }
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) cur_ans+=dfs(S,INF);
        return cur_ans;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        scanf("%d",&tt);
        while(tt--)
        {
            memset(head,0,sizeof(head));
            tot=0;
            t=1;
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            {
                cin>>node[i].a>>node[i].b>>node[i].c>>node[i].d;
                if(node[i].b=='M') tot++;
            }
            int num1=1,num2=1;
            for(int i=1;i<=n;i++)
            {
                if(node[i].b=='M') 
                    node[i].id=num1,num1++,add(S,node[i].id,1);
                else 
                    node[i].id=num2+tot,num2++,add(node[i].id,T,1);
            }
            // for(int i=1;i<=n;i++)
            //     printf("id=%d
    ",node[i].id);
            for(int i=1;i<=n;i++)
            {
                for(int j=i+1;j<=n;j++)
                {
                    if(i==j) continue;
                    int flag=0;
                    if(abs(node[i].a-node[j].a)<=40) flag++;//cout<<1<<endl;
                    if(node[i].b!=node[j].b) flag++;//cout<<2<<endl;
                    if(node[i].c==node[j].c) flag++;//cout<<3<<endl;
                    if(node[i].d!=node[j].d) flag++;//cout<<4<<endl;
                    if(flag!=4) continue;
                    if(node[i].b=='M') add(node[i].id,node[j].id,1);
                    else add(node[j].id,node[i].id,1);
                }
            }
            printf("%d
    ",n-dinic());
        }
        return 0;
    }
    

    例题29 出租车

    有m个人从城市的不同位置出发,到达它们各自的目的地。已知每个人的出发时间,出发地点和目的地。你的任务是用尽量少的出租车送他们,使得每次出租车接待客人时,至少能提前一分钟到达他所在的位置。注意,为了满足这一条件,要么这个人是这辆出租车接送的第一个人,要么在接送完上一个人后,有足够的时间从上一个目的地开到这里。

    就是DAG最小路径覆盖。(点不相交)这个的答案等于节点个数-最大匹配。(具体证明可以看上面知识点的链接)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #define MAXN 100010
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,t,S,T,tot,cnt1,cnt2;
    int id[100][100],X[MAXN],Y[MAXN],tt[MAXN];
    int head[MAXN],done[100][100],dis[2000][2000],dep[MAXN],cur[MAXN];
    int move_x[4]={0,0,1,-1},move_y[4]={1,-1,0,0};
    char a[100][100];
    struct Edge{int nxt,to,dis;}edge[200010];
    inline void add(int from,int to,int dis)
    {
        // printf("[%d %d] %d
    ",from,to,dis);
        edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
    }
    inline bool bfs()
    {
        memset(dep,0x3f,sizeof(dep));
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(S);
        dep[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dep[v]==0x3f3f3f3f&&edge[i].dis)
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        if(dep[T]==0x3f3f3f3f) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(x==T||!f) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            if(dep[edge[i].to]==dep[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
            {
                used+=w,f-=w;
                edge[i].dis-=w,edge[i^1].dis+=w;
                if(!f) break;
            }
        }
    
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) cur_ans+=dfs(S,(int)1e9);
        return cur_ans;
    }
    inline void init(int K,int x,int y)
    {
        memset(done,0,sizeof(done));
        queue<int>qx,qy;
        memset(dis[K],0x3f,sizeof(dis[K]));
        qx.push(x),qy.push(y);
        done[x][y]=1;
        dis[K][id[x][y]]=0;
        while(!qx.empty())
        {
            int u=qx.front(),v=qy.front();
            qx.pop(),qy.pop();
            for(int i=0;i<4;i++)
            {
                int xx=u+move_x[i],yy=v+move_y[i];
                if(done[xx][yy]) continue;
                done[xx][yy]=1;
                if(xx<1||xx>n||yy<1||yy>m||a[xx][yy]!='.') continue;
                dis[K][id[xx][yy]]=dis[K][id[u][v]]+1;
                qx.push(xx),qy.push(yy);
            }
        }
    }
    inline bool check(int x)
    {
        memset(head,0,sizeof(head));
        t=1,S=0,T=50000;
        int kkk=n*m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(a[i][j]=='.')
                    add(S,id[i][j],1);
        for(int i=1;i<=cnt2;i++)
        {
            for(int j=1;j<=x;j++) tt[j]=++kkk;
            for(int j=1;j<x;j++) add(tt[j],tt[j+1],INF);
            for(int j=1;j<=x;j++) add(tt[j],T,1);
            for(int p=1;p<=n;p++)
                for(int q=1;q<=m;q++)
                    if(a[p][q]=='.'&&dis[i][id[p][q]]<=x)
                        add(id[p][q],tt[dis[i][id[p][q]]],1);
        }
        int cur_ans=dinic();
        if(cur_ans==cnt1) return true;
        else return false;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                cin>>a[i][j];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                id[i][j]=++tot;
                if(a[i][j]=='.') ++cnt1;
                if(a[i][j]=='D') ++cnt2,X[cnt2]=i,Y[cnt2]=j;
            }
        for(int i=1;i<=cnt2;i++) init(i,X[i],Y[i]);
        int l=0,r=2000,ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            // printf("l=%d r=%d mid=%d
    ",l,r,mid);
            if(check(mid)==true) ans=mid,r=mid-1;
            else l=mid+1;
        }
        if(ans==-1) printf("impossible
    ");
        else printf("%d
    ",ans);
        return 0;
    }
    

    例题30 网络扩容

    给定一个有向网络,每条边都有一个容量。问是否存在一个从点1到点N(N<=100),流量为C的流。如果不存在,是否可以恰好修改一条弧的容量,使得而存在这样的流?如果可以通过修改达到要求,还需要输出修改的是哪些边。

    首先如果最大流就满足大于等于C,那么显然肯定是可以的。否则需要修改的一定要尽可能的小,那么就一定是最小割里面的边(因为最小割等于最大流,其实一条最小割就是它最大流增广路径上的,最小的边,所以一定是通过修改一定在最小割上面的边来使得最大流的增加)。
    我们依次更改这些流,然后再跑dinic,看看是否满足题意即可。
    但是注意我们修改的一定是初始的容量(所以为奇数的边是不合法的),而且为了不T,要加两个优化——1、求完最大流的时候把流量留着,以后在他的基础上进行增广;2、没有必要每次都求出最大流,增广到流量至少为C的时候停下来就行了。然后还有,因为修改过了之后(变成INF)我们就没有让它流满,所以满流的限制要去了。
    哦对了,末尾还要输出一个空行。。。。

    最后特别感谢maomao9173帮忙找锅QAQ

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<vector>
    #define int long long
    #define S 1
    #define T n
    #define MAXN 210
    #define INF 2147483647
    using namespace std;
    int n,m,tot,tt=1,top,cnt,flow,kase;
    int head[MAXN],low[MAXN],dfn[MAXN],st[MAXN],in[MAXN],c[MAXN],cur[MAXN],dis[MAXN];
    struct Edge{int nxt,to,dis;}edge[MAXN*MAXN];
    struct Line{int u,v,w;}line[MAXN*MAXN];
    struct Node{int u,v;};
    vector<Node>ans;
    inline bool cmp(struct Node x,struct Node y)
    {
        if(x.u==y.u) return x.v<y.v;
        return x.u<y.u;
    }
    inline void add(int from,int to,int dis)
    {
        edge[++tt].nxt=head[from],edge[tt].to=to,edge[tt].dis=dis,head[from]=tt;
        edge[++tt].nxt=head[to],edge[tt].to=from,edge[tt].dis=0,head[to]=tt;
    }
    inline bool bfs()
    {
        for(int i=0;i<=T;i++) dis[i]=INF;
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(S);
        dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]==INF&&edge[i].dis)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[T]==INF) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(x==T||!f) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            if(dis[edge[i].to]==dis[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
            {
                used+=w,f-=w;
                edge[i].dis-=w,edge[i^1].dis+=w;
                if(!f) break;
            }
        }
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) 
        {
            cur_ans+=dfs(S,INF);
            if(cur_ans>=flow) break;
        }
        return cur_ans;
    }
    inline void tarjan(int x)
    {
        low[x]=dfn[x]=++tot;
        in[x]=1,st[++top]=x;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].dis==0) continue;
            if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
            else if(in[v]) low[x]=min(low[x],dfn[v]);
        }
        if(dfn[x]==low[x])
        {
            int v;cnt++;
            do{v=st[top--];in[v]=0;c[v]=cnt;}while(v!=x);
        }
    }
    inline void print()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j;j=edge[j].nxt)
                printf("[%lld %lld] %lld
    ",i,edge[j].to,edge[j].dis);
        }
        printf("
    ");
    }
    inline void init()
    {
        memset(head,0,sizeof(head));
        tt=1;
        for(int i=1;i<=m;i++)
            add(line[i].u,line[i].v,line[i].w);
    }
    inline void pre()
    {
        ans.clear();
        memset(head,0,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(in,0,sizeof(in));
        memset(c,0,sizeof(c));
        tt=1;
        tot=top=cnt=0;
    }
    signed main() {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        while (cin >> n >> m >> flow) {
        	if(kase!=0) puts("");
            if(n==0&&m==0&&flow==0) break;
            pre();
            for(int i=1;i<=m;i++)
            {
                scanf("%lld%lld%lld",&line[i].u,&line[i].v,&line[i].w);
                add(line[i].u,line[i].v,line[i].w);
            }
            int ret=dinic();
            printf("Case %lld: ",++kase);
            if(ret>=flow) {printf("possible");continue;}
            for(int i=1;i<=n;i++)
                if(!dfn[i])
                    tarjan(i);
            bool flag=false;
            for(int i=1;i<=n;i++)
                for(int j=head[i];j;j=edge[j].nxt)
                {
                    if(j%2==1) continue;
                    if(c[i]!=c[edge[j].to])
                    {
                        init();
                        edge[j].dis=INF;
                        if(dinic()>=flow) 
                            ans.push_back((Node){i,edge[j].to}),flag=true;
                    }
                }
            if(flag==false) {printf("not possible");continue;}
            sort(ans.begin(),ans.end(),cmp);
            printf("possible option:");
            printf("(%lld,%lld)",ans[0].u,ans[0].v);
            for(int i=1;i<ans.size();i++)
                printf(",(%lld,%lld)",ans[i].u,ans[i].v);
    	}
        return 0;
    }
    
    

    例题31 运送超级计算机

    宇宙中有n个星球,你的任务是用最短的时间把k个超级计算机从星球S运送到星球T。每个超级计算机需要一整艘飞船来运输。行星之间有m条双向隧道,每条隧道需要一天的时间来通过,且不能有两艘飞船同时使用同一条隧道。隧道不会连接两个相同的行星,且每一对行星之间最多只有一条隧道。
    如果有解,输出当天移动的飞船数,以及当天编号为i的飞船到达的行星。

    假定答案为x,我们把每个点u拆成x+1个,分别对应每天的相应节点。对于原图中的相邻节点a和b,在新图中添加一条从(a_i)(b_{i+1})的边,容量为1,再添加一条(b_i)(a_{i+1})的边,容量也为1。
    然后因为可以不移动,所以每个节点都向自己下一个时间的节点连接一条流量为INF的边就行了。

    我们逐步增大天数,然后在上次求出来的最大流的基础上继续增广,直到流量达到K。
    有一个需要特判:如果在某时刻(a_i->b_{i+1})(a_{i+1}->b_i)同时有流量,是不符合题意的,它们互相抵消,就没有了。我们只处理有一个方向的流量即可。
    而这样的话每次T都会改变,那么我们就不要超级源点了,每次都更改T为当前状态的终点即可。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #include <queue>
    #include<iostream>
    using namespace std;
    
    typedef long long ll;
    const int N = 5005;
    const int M = 100005;
    const int INF = 0x3f3f3f3f;
    int n, m, k, s, t, Day;
    int u[N], v[N];
    
    struct Dinic{
        int ec, head[N], first[N], que[N], lev[N];
        int Next[M], to[M], v[M];
    
        void init() {
            ec = 0;
            memset(first, -1, sizeof(first));
        }
    
        void addEdge(int a,int b,int c) {
            to[ec] = b;
            v[ec] = c;
            Next[ec] = first[a];
            first[a] = ec++;
    
            to[ec] = a;
            v[ec] = 0;
            Next[ec] = first[b];
            first[b] = ec++;
        }
    
        int BFS() {
            int kid, now, f = 0, r = 1, i;
            memset(lev, 0, sizeof(lev));
            que[0] = s, lev[s] = 1;
            while (f < r) {
                now = que[f++];
                for (i = first[now]; i != -1; i = Next[i]) {
                    kid = to[i];    
                    if (!lev[kid] && v[i]) {
                        lev[kid] = lev[now] + 1;    
                        if (kid == t) return 1;
                        que[r++] = kid;
                    }
                }
            }
            return 0;
        }
    
        int DFS(int now, int sum) {
            int kid, flow, rt = 0;
            if (now == t || sum == 0) return sum;
            for (int i = head[now]; i != -1 && rt < sum; i = Next[i]) {
                head[now] = i;  
                kid = to[i];
                if (lev[kid] == lev[now] + 1 && v[i]) {
                    flow = DFS(kid, min(sum - rt, v[i]));
                    if (flow) {
                        v[i] -= flow;
                        v[i^1] += flow;
                        rt += flow;
                    } else lev[kid] = -1;   
                }           
            }
            return rt;
        }
    
        int dinic(int num, int need) {
            int ans = 0;
            while (BFS()) {
                for (int i = 0; i <= num; i++) {
                    head[i] = first[i];
                }           
                ans += DFS(s, need - ans);
                if (ans == need) return ans;
            }
            return ans;
        }   
    }din;
    
    void input() {
        for (int i = 0; i < m; i++) {
            scanf("%d %d", &u[i], &v[i]);   
        }
    }
    
    void solve() {
        int sum = 0;
        Day = 1;
        int tt = t;
        while (sum < k) { //天数往后加,直到运送的总数到达目标数
            for (int i = 1; i <= n; i++) {
                din.addEdge((Day - 1) * n + i, Day * n + i, INF);
                //表示这一天这艘飞船原地不动
            }
            for (int i = 0; i < m; i++) {
                din.addEdge((Day - 1) * n + u[i], Day * n + v[i], 1);   
                //飞船在这一天从u[i]移动到v[i]
                din.addEdge((Day - 1) * n + v[i], Day * n + u[i], 1);   
                //飞船在这一天从v[i]移动到u[i]
            }
            t = tt + Day * n; //每过一天都要加一层图,所以每天的t点都不一样
            sum += din.dinic(Day * n + n, k - sum); //加上这一天到达n星球的飞船数
            if (sum == k) break;
            Day++;
        }
        printf("%d
    ", Day);
        int idx = 0;
        vector<int> location(k, s);
        for(int d = 1; d <= Day; d++){
            idx += n * 2; //每天跳过当天不动的飞船,无需判断
            vector<int> moved(k, 0);
            vector<int> a, b;
            for(int i = 0; i < m; i++){ 
                //每次枚举两个点,为了避免同一天飞船A从u到v,飞船B从v到u
                int f1= din.v[idx^1]; //idx^1表示idx点对应的流量
                idx += 2;
                int f2= din.v[idx^1]; 
                idx += 2;
                if(f1 && !f2) { //不能两条边同时有流量
                    a.push_back(u[i]); 
                    b.push_back(v[i]);
                }
                if(!f1 && f2) {
                    a.push_back(v[i]); 
                    b.push_back(u[i]);
                }
            }
            printf("%d", (int)a.size());
            for(int i = 0; i < a.size(); i++){
                for(int j = 0; j < k; j++) {
                    if(!moved[j] && location[j] == a[i]){
                        moved[j] = 1;
                        printf(" %d %d", j + 1, b[i]);
                        location[j] = b[i];
                        break;
                    }
                }
            }puts("");
        }
    }
    
    int main() {
        // freopen("ce.in","r",stdin);
        // freopen("ce.out","w",stdout);
        while (scanf("%d %d %d %d %d", &n, &m, &k, &s, &t) == 5) {
            din.init();
            input();    
            solve();
        }
        return 0;
    }
    
    

    例题32 足球联赛

    有n个队伍进行比赛,每个队伍需要打的比赛数目相同。每场比赛都没有平局。给出每个队伍目前的胜利场数和失败场数,以及每两个队伍还剩下的比赛场数。确定所有可能得冠军的球队。(获胜场数最多的得冠军,可以并列)

    如果一个队伍在接下来的比赛中都胜利,而且其他队伍可以通过权衡、转化胜利情况,使得获胜场次均不大于该队的获胜场次,那么这个队伍就有可能获得冠军。权衡、转化的过程,往往可以通过最大流来解决

    那么我们考虑建图。先从源点给每支队伍连接它们已经胜利的场次的容量的边,然后给每场比赛都建立一个结点,并向它的参赛队伍连流量为INF的边。然后从源点向比赛连接还要比赛的次数的容量的边。然后给我们当前枚举的点连接它能获胜就获胜的获胜次数,给其他点限制流量,不超过指定点的最大获胜次数。然后如果从S出发的边都满流的话,这种情况就有合法解了——即该队有可能获得冠军。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #define S 0
    #define T n*n+n+1
    #define MAXN 100010
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,t,tt,kase;
    int head[MAXN],dis[MAXN],cur[MAXN],win[MAXN],lose[MAXN],sum[30][30],more[MAXN];
    struct Edge{int nxt,to,dis;}edge[MAXN<<1];
    inline void add(int from,int to,int dis)
    {
        // printf("[%d %d] %d
    ",from,to,dis);
    	edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
    }
    inline bool bfs()
    {
        memset(dis,0x3f,sizeof(dis));
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(S);
        dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]==0x3f3f3f3f&&edge[i].dis)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[T]==0x3f3f3f3f) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(x==T||!f) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            if(dis[edge[i].to]==dis[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
            {
                used+=w,f-=w;
                edge[i].dis-=w,edge[i^1].dis+=w;
                if(!f) break;
            }
        }
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) cur_ans+=dfs(S,INF);
        return cur_ans;
    }
    inline int match(int x,int y){return (x-1)*n+y;}
    inline int team(int x){return n*n+x;}
    inline bool solve(int x)
    {
    	memset(head,0,sizeof(head));
    	t=1;
    	int all=0;
    	int total=more[x]+win[x];
    	for(int i=1;i<=n;i++) 
    	{
    		for(int j=i+1;j<=n;j++)
    		{
    			if(i==x||j==x) continue;
    			all+=sum[i][j];
    			add(S,match(i,j),sum[i][j]);
    			add(match(i,j),team(i),INF),add(match(i,j),team(j),INF);
    		}
    	}
    	for(int i=1;i<=n;i++) 
    	{
    		if(total<win[i]) return false;
    		add(team(i),T,total-win[i]);
    	}
    	// dinic();
    	// for(int i=head[S];i;i=edge[i].nxt)
    	// 	  if(edge[i].dis) return false;
    	// return true;
    	if(dinic()==all) return true;
    	return false;
    }
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("ce.in","r",stdin);
    	// freopen("ce.out","w",stdout);
    	#endif
    	scanf("%d",&tt);
    	while(tt--)
    	{
    		memset(more,0,sizeof(more));
    		scanf("%d",&n);
    		for(int i=1;i<=n;i++) scanf("%d%d",&win[i],&lose[i]);
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=n;j++)
    			{
    				scanf("%d",&sum[i][j]);
    				more[i]+=sum[i][j];
    			}
    		bool flag=false;
    		for(int i=1;i<=n;i++)
    		{
    			if(solve(i)) 
    			{
    				if(flag==false) printf("%d",i),flag=true;
    				else printf(" %d",i);
    			}
    		}
    		puts("");
    	}
    	return 0;
    } 
    

    例题33 收集者的难题

    Bob和他的朋友从糖果包装里收集贴纸,这些朋友每人手里都有一些(可能会有重复的)贴纸,并且只和别人交换他所没有的贴纸。贴纸只会一对一交换。
    Bob比这些朋友们更聪明,因为他意识到只和别人交换自己没有的贴纸并不总是最优的,在某些情况下,换来一张重复的贴纸更划算。
    假设Bob的朋友只和Bob交换(他们之间不交换),并且这些朋友只会出让手里的重复贴纸来交换他们没有的不同贴纸。你的任务是帮助Bob算出他最终可以得到的不太用贴纸的最大数量。

    还是那句话,看到转化转换,就想到最大流。
    我们用n-1个点表示每个除了Bob之外的人,再用m个点表示每个物品,同时添加一个S和T,从S向每个物品连边,容量为Bob拥有该物品的数量。如果Bob以外的某个人i拥有至少两个物品j,就从i向物品j连边,容量为拥有数量-1。如果i没有物品j,那么就从物品j向i连边,容量为1。最后从物品向T连边,容量为1,跑最大流即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #define S 0
    #define T n+m
    #define MAXN 100010
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,t,tt,kase;
    int head[MAXN],dis[MAXN],cur[MAXN],sum[30][30];
    struct Edge{int nxt,to,dis;}edge[MAXN<<1];
    inline void add(int from,int to,int dis)
    {
        edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
    }
    inline bool bfs()
    {
        memset(dis,0x3f,sizeof(dis));
        memcpy(cur,head,sizeof(head));
        queue<int>q;
        q.push(S);
        dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]==0x3f3f3f3f&&edge[i].dis)
                {
                    dis[v]=dis[u]+1;
                    q.push(v);
                }
            }
        }
        if(dis[T]==0x3f3f3f3f) return false;
        return true;
    }
    inline int dfs(int x,int f)
    {
        if(x==T||!f) return f;
        int used=0,w;
        for(int i=cur[x];i;i=edge[i].nxt)
        {
            cur[x]=i;
            if(dis[edge[i].to]==dis[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
            {
                used+=w,f-=w;
                edge[i].dis-=w,edge[i^1].dis+=w;
                if(!f) break;
            }
        }
        return used;
    }
    inline int dinic()
    {
        int cur_ans=0;
        while(bfs()) cur_ans+=dfs(S,(int)1e9);
        return cur_ans;
    }
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("ce.in","r",stdin);
    	freopen("ce.out","w",stdout);
    	#endif
    	scanf("%d",&tt);
    	while(tt--)
    	{
    		memset(head,0,sizeof(head));
    		memset(sum,0,sizeof(sum));
    		t=1;
    		scanf("%d%d",&n,&m);
    		for(int i=1;i<=n;i++)
    		{
    			
    			int k,x;
    			scanf("%d",&k);
    			for(int j=1;j<=k;j++)
    			{
    				scanf("%d",&x);
    				sum[i][x]++;
    			}
    			if(i==1)
    				for(int j=1;j<=m;j++) add(S,j,sum[i][j]);
    			else
    				for(int j=1;j<=m;j++)
    				{
    					if(sum[i][j]==0) add(j,i+m-1,1);
    					if(sum[i][j]>=2) add(i+m-1,j,sum[i][j]-1);
    				}
    		}
    		for(int i=1;i<=m;i++) add(i,T,1);
    		printf("Case #%d: %d
    ",++kase,dinic());
    	}
    	return 0;
    }
    

    例题34 生产销售规划

    某公司生产一种元素。给出该元素在未来M个月中每个月的单位售价,最大产量,生产成本,最大销售量,以及最大储存时间(过期报废,不过可以储存任意多的量)。你的任务是计算出公司能够赚到的最大利润。
    不固定流量的最小费用流
    方法是寻找增广路的时候,如果dis[T]>0就不增广就行了。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #define S 0
    #define T 2*n+1
    #define MAXN 100010
    #define INF 0x3f3f3f3f3f3f3f3f
    #define int long long
    using namespace std;
    int n,m,t=1,c,f,tt,pay,kase;
    int head[MAXN],dis[MAXN],done[MAXN],pre_e[MAXN],pre_v[MAXN];
    struct Edge{int nxt,to,dis,cost;}edge[MAXN<<1];
    inline void add(int from,int to,int dis,int cost)
    {
        // printf("[%lld %lld] %lld %lld
    ",from,to,dis,cost);
        edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,edge[t].cost=cost,head[from]=t;
        edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,edge[t].cost=-cost,head[to]=t;
    }
    inline bool spfa()
    {
        queue<int>q;
        for(int i=0;i<=T;i++) dis[i]=INF;
        memset(done,0,sizeof(done));
        q.push(S);done[S]=1;dis[S]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();done[u]=0;
            for(int i=head[u];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]>dis[u]+edge[i].cost&&edge[i].dis)
                {
                    dis[v]=dis[u]+edge[i].cost;
                    pre_e[v]=i,pre_v[v]=u;
                    if(!done[v])
                        done[v]=1,q.push(v);
                }
            }
        }
        if(dis[T]>0) return false;     
        int flow=INF;
        for(int i=T;i!=S;i=pre_v[i]) flow=min(flow,edge[pre_e[i]].dis);
        for(int i=T;i!=S;i=pre_v[i]) edge[pre_e[i]].dis-=flow,edge[pre_e[i]^1].dis+=flow;
        f+=flow,c+=dis[T]*flow;
        return true;
    }
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        scanf("%lld",&tt);
        while(tt--)
        {
            memset(head,0,sizeof(head));
            c=f=0;
            t=1;
            scanf("%lld%lld",&n,&pay);
            for(int i=1;i<=n;i++)
            {
                int pro_v,pro_sum,sale_v,sale_sum,e;
                scanf("%lld%lld%lld%lld%lld",&pro_v,&pro_sum,&sale_v,&sale_sum,&e);
                add(S,i,pro_sum,pro_v);
                add(i+n,T,sale_sum,-sale_v);
                add(i,i+n,INF,0);
                for(int j=1;j<=e&&i+n+j<=2*n;j++) add(i,i+n+j,INF,pay*j);
            }
            while(spfa());
            printf("Case %lld: %lld
    ",++kase,-c);
        }
        return 0;
    }
    
  • 相关阅读:
    解决Tomcat9打印台乱码问题
    分治思想与归并排序
    linux下libuv库安装教程
    Linux init
    栈和堆上的内存分配和回收
    Python帮助文档中Iteration iterator iterable 的理解
    Qt基本框架介绍
    PyQt5+Python3.5.2-32bit开发环境搭建
    常用网站
    [Repost]Events and Signals in PyQt4
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10851313.html
Copyright © 2011-2022 走看看