【刷题记录】网络流24题等

令人崩溃的五道题

首先是网络流24题中的前五题

1.飞行员配对问题

标准的二分图匹配,这里采用时间复杂度最优秀O(sqrt(E)V)的网络流做法

建立源点s汇点t分别连接至二分图的两个部分

所有边权设置为1

跑最大流即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#define N 105
#define next nico
int head[N],to[N*N*2],next[N*N*2],dis[N*N*2],tot=1,d[N],s=0,t,n,m;
void add(int x,int y, int z)
{
    to[++tot]=y;
    dis[tot]=z;
    next[tot]=head[x];
    head[x]=tot;
}
int bfs()
{
    memset(d,0,sizeof(d));
    queue <int> q;
    d[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int i = head[x]; i; i = next[i])
        {
            int des = to[i];
            if(dis[i]&&!d[des])
            {
                d[des]=d[x]+1;
                q.push(des);
            }
        }
    }
    return d[t];
}
int dfs(int x,int v)
{
    if(x==t||v==0)return v;
    int ans = 0;
    for(int i = head[x]; i ; i = next[i])
    {
        int des = to[i];
        if(d[des]==d[x]+1)
        {
            int f = dfs(des,min(dis[i],v));
            v -= f;
            dis[i]-=f;
            dis[i^1]+=f;
            ans += f;
        }
    }
    return ans;
}
int main()
{
    scanf("%d%d",&m,&n);
    int x=0,y=0;
    while(x!=-1)
    {
        scanf("%d%d",&x,&y);
        add(x,y,1);
        add(y,x,0);
    }
    for(int i = 1; i <= m; i ++)
    {
        add(n+2,i,1);
        add(i,n+2,0);
    }
    for(int i = m +1; i <= n ; i++)
    {
        add(i,n+1,1);
        add(n+1,i,0);
    }
    s=n+2;t=n+1;
    int ans = 0;
    while(bfs())
    {
        ans +=dfs(s,0x7f7f7f7f);
    }
    if(ans!=0)
    {
    printf("%d
",ans);
    for(int i = 1; i <= m; i ++)
    {
        for(int j = head[i];j;j=next[j])
        {
            if(dis[j]==0&&to[j]<=n)
            {
                printf("%d %d
",i,to[j]);
            }
        }
    }
    }
    else puts("No Solution!");
}

2.太空飞行计划问题

最大边权子图的模板题问题

将模型转化成二分图,对于正权点连接源点,负权点连接汇点

跑最大流即可

答案是正权点数总和减去最大流

选取的点是阻塞流到达的点

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
#define N 1000
#define next nico
int m, n;
const int inf = 0x0f7f7f7f;
int head[N], next[N * N], to[N], tot = 1, val[N];
int sum;
void add(int x, int y, int z)
{
    to[++tot] = y;
    next[tot] = head[x];
    val[tot] = z;
    head[x] = tot;
}
int d[N];
int bfs(int s, int t)
{
    memset(d, 0, sizeof(d));
    d[s] = 1;
    queue<int> q;
    q.push(s);
    while (!q.empty())
    {
        int x = q.front();
        q.pop();
        for (int i = head[x]; i; i = next[i])
            if (val[i] && !d[to[i]])
            {
                d[to[i]] = d[x] + 1;
                q.push(to[i]);
            }
    }
    return d[t];
}
int dfs(int x, int v, int t)
{
    if (x == t || v == 0)
        return v;
    int ans = 0;
    for (int i = head[x]; i; i = next[i])
        if (d[x] + 1 == d[to[i]])
        {
            int l = dfs(to[i], min(val[i], v), t);
            v-=l;
            ans += l;
            val[i] -= l;
            val[i ^ 1] += l;
        }
    return ans;
}

int main()
{
    scanf("%d%d", &m, &n);
    char buf[10001];
    cin.getline(buf, 10000);
    for (int i = 1; i <= m; i++)
    {
        memset(buf,0,sizeof(buf));
        cin.getline(buf, 10000);
        int p = 0, a;
        while (sscanf(buf + p, "%d", &a) == 1)
        {
            if (p != 0)
            {
                add(i, a + m, inf);
                add(a + m, i, 0);
            }
            else
            {
                add(n + m + 1, i, a);
                add(i, n + m + 1, 0);
                sum+=a;
            }
            if (a == 0)
                p++;
            else
                while (a)
                {
                    a /= 10;
                    p++;
                }
            p++;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        int a;
        scanf("%d", &a);
        add(m + i, n + m + 2, a);
        add(n + m + 2, m + i, 0);
    }
    int ans = 0;
    while (bfs(n + m + 1, n + m + 2))
    {
        ans += dfs(n + m + 1, inf, n + m + 2);
    }

    for (int j = head[n + m + 1]; j; j = next[j])
        if (d[to[j]] != 0)
        {
            printf("%d ", to[j]);
        }
    puts("");
    for (int j = head[n + m + 2]; j; j = next[j])
        if (d[to[j]]!= 0)
        {
            printf("%d ", to[j]-m);
        }
    puts("");
    printf("%d
", sum - ans);
}

3.最小路径覆盖问题

转化成二分图匹配问题即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
#define N 400
#define next nico
int head[N],to[30005],next[30005],tot=1,dis[30005],n,m,d[N],s,t;
bool vis[N];
void add(int x,int y,int z)
{
    to[++tot]=y;
    next[tot]=head[x];
    dis[tot]=z;
    head[x]=tot;
}
int bfs()
{
    memset(d,0,sizeof(d));
    queue<int> q;
    q.push(s);
    d[s]=1;
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int i = head[x];i;i=next[i])
        if(dis[i]&&!d[to[i]])
        {
            d[to[i]]=d[x]+1;
            q.push(to[i]);
        }
    }
    return d[t];
}
int dfs(int x,int v)
{
    if(x==t||v==0)return v;
    int ans = 0;
    for(int i = head[x];i;i = next[i])
    if(d[to[i]]==d[x]+1)
    {   
        int l = dfs(to[i],min(v,dis[i]));
        dis[i]-=l;
        dis[i^1]+=l;
        ans+=l;
        v-=l;
    }
    return ans;
}
void printans(int x)
{
    vis[x]=1;
    printf("%d ",x);
    for(int i = head[x];i;i=next[i])
    {
        if(!dis[i]&&!vis[to[i]-n]&&to[i]<=n*2)
        {
            printans(to[i]-n);
        }
    }
}
int main()
{
    #ifdef TEST
        freopen("test.in","r",stdin);
    #endif
    scanf("%d%d",&n,&m);
    for(int i = 1 ; i <= m ; i ++)
    {
        int a,b,c=1;
        scanf("%d%d",&a,&b);
        add(a,b+n,c);
        add(b+n,a,0);
    }
    s = 2*n+1;t = s+1;
    for(int i = 1; i <= n ; i ++)
    {
        add(s,i,1);
        add(i,s,0);
        add(i+n,t,1);
        add(t,i+n,0);
    }
    int ans = 0;
    while(bfs())
    {
        ans += dfs(s,0x7f7f7f7f);
    }
    for(int i = 1; i <= n; i ++)
    if(!vis[i])
    {
        printans(i);
        putchar('
');
    }
    printf("%d",n-ans);
}

4.魔术球问题

可以贪心做

我采取将其转化成最小路径覆盖问题解决

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <map>
#include <cmath>
using namespace std;
#define next nico
#define N 5000
#define M 2000
int head[N],d[N],next[N*N],to[N*N],val[N*N],tot=1;
int ans[100][N],tot1;
int vis[N];
void add(int a , int b ,int c)
{
    to[++tot]=b;
    next[tot]=head[a];
    val[tot]=c;
    head[a]=tot;
}
int s,t;
void getans(int x)
{
    ans[tot1][++ans[tot1][0]]=x-2;
    vis[x-2]=1;
    for(int i = head[x];i;i=next[i])
    if(!val[i]&&to[i]>M&&!vis[to[i]-M])
    {
        getans(to[i]-M+2);
    }
}
int bfs()
{
    queue <int> q;
    q.push(s);
    memset(d,0,sizeof(d));
    d[s]=1;
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        for(int i = head[x]; i; i = next[i])
        if(val[i]&&!d[to[i]])
        {
            d[to[i]]=d[x]+1;
            q.push(to[i]);
        }
    }
    return d[t];
}
int dfs(int x,int v)
{
    if(x==t||v==0)return v;
    int ans = 0;
    for(int i = head[x]; i; i = next[i])
    if(d[to[i]]==d[x]+1)
    {
        int l = dfs(to[i],min(val[i],v));
        v-=l;
        ans+=l;
        val[i]-=l;
        val[i^1]+=l;
    }
    return ans;
}
int main()
{
    int n,i;
    scanf("%d",&n);
    s=1;t=2;  int sum=0;
    for(i = 1; ; i ++)
    {
        add(s,i+2,1);
        add(i+2,s,0);
        add(t,i+M,0);
        add(i+M,t,1);
        for(int j = 1; j < i ; j ++)
        {
            int p = (int)sqrt((double)i+j);
            if(p*p==i+j)
            {
                add(j+2,i+M,1);
                add(i+M,j+2,0);
            }
        }
        while(bfs())
        {
            sum+=dfs(s,0x7f7f7f7f);
        }
        if(i-sum>n)break;
        tot1 = 0;
        memset(vis,0,sizeof(vis));
        for(int j = 1; j <= i; j ++)
        if(!vis[j])
        {
            tot1++;
            ans[tot1][0]=0;
            getans(j+2);
        }
    }
    printf("%d
",i-1);
    for(int i = 1 ; i<= tot1; i ++)
    {
    for(int j = 1; j <= ans[i][0];j ++)
    {
        printf("%d ",ans[i][j]);
    }
    puts("");
    }
}

5.圆桌问题

与普通的二分图匹配不同,连接源点边权赋值为容纳人数

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
#define N 1000
#define next nico
int head[N],next[N*N],to[N*N],v[N*N],tot=1,s,t;
int* val = v;
int d[N];
void add(int x,int y,int z)
{
    to[++tot]=y;
    v[tot]=z;
    next[tot]=head[x];
    head[x]=tot;
}
int bfs()
{
    memset(d,0,sizeof(d));
    queue<int>q;
    q.push(s);
    d[s]=1;
    while(!q.empty())
    {
        int x =q.front();
        q.pop();
        for(int i = head[x];i;i=next[i])
        if(!d[to[i]]&&v[i])
        {
            d[to[i]]=d[x]+1;
            q.push(to[i]);
        }
    }
    return d[t];
}
int dfs(int x,int v)
{
    if(x==t||v==0)return v;
    int ans = 0;
    for(int i = head[x]; i ; i = next[i])
    if(d[to[i]]==d[x]+1)
    {
        int l = dfs(to[i],min(v,val[i]));
        val[i]-=l;
        val[i^1]+=l;
        v-=l;
        ans += l;
    }
    return ans ;
}
int main()
{
    int m,n,sum=0;
    scanf("%d%d",&m,&n);
    s = m+n+1;
    t = s+1;
    for(int i = 1 ; i <= m; i ++)
    {
        int x;
        scanf("%d",&x);
        add(s,i,x);
        add(i,s,0);
        sum+=x;
    }
    for(int i = 1; i <= n ; i ++)
    {
        int x;
        scanf("%d",&x);
        add(i+m,t,x);
        add(t,i+m,0);
    }
    for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= m; j ++)
    {
        add(j,i+m,1);
        add(i+m,j,0);
    }
    int ans = 0;
    while(bfs())
    {
        ans += dfs(s,0x7f7f7f7f);
    }
    if(ans == sum)
    {
        puts("1");
        for(int i = 1; i <= m; i ++)
        {
        for(int j = head[i];j;j=next[j])
        {
            if(v[j]==0&&to[j]<=m+n)
            {
                printf("%d ",to[j]-m);
            }
        }
        puts("");
        }
    }
    else
    {
        putchar('0');
    }
}
原文地址:https://www.cnblogs.com/akonoh/p/10220051.html