zoukankan      html  css  js  c++  java
  • 【这是一个可能永远填不完的坑】网络流24题

      看到那么多大佬都开坑刷题,那我也随波逐流一下。。。虽然保不准什么时候就弃掉了。。

      进度:

    8/24


      1、餐巾计划问题(费用流)

      题目传送门:https://www.luogu.org/problemnew/show/P1251

      这道题还是比较思维的。。。(然而说白了都是套路)

      显然我们可以把餐巾使用量看作网络的流量,把花费看作网络的费用。

      这道题主要的难点就在于如何保证每天一定有ai条餐巾用,以及处理干净餐巾的来源与脏餐巾的去向。因为只能通过源汇来控制整个网络的流量(即餐巾的使用量),所以我们可以把使用一条干净餐巾的过程拆成两个部分:(1)把一条干净的餐巾流进汇点;(2)让一条脏餐巾从源点流出。

      于是把一天拆成两个点,一个用来接受脏餐巾,一个用来使用干净的餐巾,然后按题意连边(把脏餐巾洗干净),跑最小费用最大流就行了。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define ll long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define inf 0x3f3f3f3f
    #define mod 1000000007
    #define eps 1e-18
    inline ll read()
    {
        ll tmp=0; char c=getchar(),f=1;
        for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1;
        for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0';
        return tmp*f;
    }
    using namespace std;
    struct edge{
        int from,to,nxt;
        ll w,flow;
    }e[1600010];
    int fir[4010],inq[4010],last[4010];
    ll dist[4010];
    int q[1600010];
    int a[2010];
    int n,S,T,tot=0;
    void add(int x,int y,ll w,ll f)
    {
        e[tot].from=x; e[tot].to=y; e[tot].w=w; e[tot].flow=f; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].from=y; e[tot].to=x; e[tot].w=-w; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int spfa()
    {
        int h=1,t=1,i;
        for(i=0;i<=T;i++)dist[i]=inf,inq[i]=0,last[i]=-1;
        q[1]=S; dist[S]=0; inq[S]=1;
        while(h<=t){
            int now=q[h++]; inq[now]=0;
            for(i=fir[now];~i;i=e[i].nxt)
                if(e[i].flow&&dist[e[i].to]>dist[now]+e[i].w){
                    dist[e[i].to]=dist[now]+e[i].w; last[e[i].to]=i;
                    if(!inq[e[i].to]){
                        q[++t]=e[i].to; inq[e[i].to]=1;
                    }
            }
        }
        if(dist[T]==inf)return 0;else return 1;
    }
    ll flow()
    {
        ll ans=inf;
        for(int i=T;i!=S;i=e[last[i]].from)ans=min(ans,e[last[i]].flow);
        for(int i=T;i!=S;i=e[last[i]].from)
            e[last[i]].flow-=ans,e[last[i]^1].flow+=ans;
        return ans;
    }
    int main()
    {
        int i;
        n=read(); S=0; T=2*n+1;
        for(i=1;i<=n;i++)a[i]=read();
        memset(fir,255,sizeof(fir));
        ll p0=read(),t1=read(),p1=read(),t2=read(),p2=read();
        for(i=1;i<=n;i++){
            add(S,i,0,a[i]); add(i+n,T,0,a[i]);
            if(i+t1<=n)add(i,i+t1+n,p1,inf);
            if(i+t2<=n)add(i,i+t2+n,p2,inf);
            add(S,i+n,p0,inf);
            if(i<n)add(i,i+1,0,inf),add(i+n,i+n+1,0,inf);
        }
        ll ans=0;
        while(spfa())ans+=flow()*dist[T];
        printf("%lld
    ",ans);
    }
    洛谷P1251

    ======================================== UPD:2018.4.28 ==================================

      

      2、家园[CTSC1999](最大流)

      题目传送门:https://www.luogu.org/problemnew/show/P2754

      我们首先可以二分转化为判定性问题:过了t时后,k人是否都能到达月球。

      然后考虑拆点,把地球/月球/每个太空站按时间拆成t个点,对于每一时刻,从第i-1时刻的点往这第i时刻的对应点连一条流量为无穷大的边(停在地球/月球/太空站),然后按照每条航线在i-1时刻到第i时刻的航行路线连一条长度为太空产容量的边(坐太空船转移)。

      最后跑最大流就能得出在前t时刻能转移旅客的最大值。

      但是我们还有一种更优秀的写法。

      我们会发现,比较第t时刻与第t+1时刻时的网络,网络的不同之处仅在于第t时刻到第t+1时刻新连的边,即前t时刻的网络的最大流不变。

      于是我们可以从小到大枚举当前时刻,在上一时刻的剩余网络中连上新边,当前时刻的最大流就等于上一时刻的最大流+当前剩余网络的最大流,这样的写法效率更佳。

      至于如何判断无解。。。我们可以发现对于每个人,地球/每个太空站最多经过一次,且在地球/太空站上停留的时间最多不超过(n+2)个时刻(等太空船转一圈最多只用(n+2)个时刻),所以如果有解,那么过了第(n+1)*(n+2)时刻,必有人到达月球。所以当t>(n+1)*(n+2)且最大流==0就无解了。

      代码:

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define ll long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define inf 0x3f3f3f3f
    #define mod 1000000007
    #define eps 1e-18
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=tmp*10+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll tmp=1; for(;b;b>>=1){if(b&1)tmp=tmp*a%mod; a=a*a%mod;} return tmp;}
    using namespace std;
    struct edge{
        int to,nxt,flow;
    }e[1000010];
    int n,m,k,S,T,tot=0;
    int a[30],r[30],s[30][30];
    int fir[250010],lv[250010],q[250010];
    void add(int x,int y,int flow)
    {
        e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int dfs(int now,int flow)
    {
        if(now==T)return flow;
        for(int i=fir[now];~i;i=e[i].nxt)
            if(e[i].flow&&lv[e[i].to]==lv[now]+1){
                int tmp=dfs(e[i].to,min(flow,e[i].flow));
                e[i].flow-=tmp; e[i^1].flow+=tmp;
                if(tmp)return tmp;
            }
        return 0;
    }
    int dinic()
    {
        int i,ans=0;
        while(1){
            for(i=0;i<=T;i++)lv[i]=0;
            int h=1,t=1; q[1]=S; lv[S]=1;
            while(h<=t){
                for(i=fir[q[h]];~i;i=e[i].nxt)
                    if(e[i].flow&&!lv[e[i].to]){
                        q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1;
                    }
                ++h;
            }
            if(!lv[T])return ans;
            int k=dfs(S,inf);
            while(k)ans+=k,k=dfs(S,inf);
        }
    }
    int main()
    {
        int i,j;
        n=read(); m=read(); k=read();
        memset(fir,255,sizeof(fir));
        for(i=1;i<=m;i++){
            a[i]=read(); r[i]=read();
            for(j=1;j<=r[i];j++)s[i][j]=read();
        }
        int t=1,ans=0;
        for(;;t++){
            S=0; T=(t+1)*(n+2)-1;
            for(i=0;i<n+2;i++)add((t-1)*(n+2)+i,t*(n+2)+i,inf);
            for(i=1;i<=m;i++){
                int now=t%r[i],nxt=now+1;
                if(!now)now=r[i];
                if(s[i][now]==-1)now=n+1;else now=s[i][now];
                if(s[i][nxt]==-1)nxt=n+1;else nxt=s[i][nxt];
                add((t-1)*(n+2)+now,t*(n+2)+nxt,a[i]);
            }
            ans+=dinic();
            if(ans>=k)break;
            if(t>(n+2)*(n+1)&&!ans){
                printf("0
    "); return 0;
            }
        }
        printf("%d
    ",t);
    }
    洛谷P2754


     ========================================UPD:2018.4.29==================================

      

      3、飞行员配对方案问题(二分图匹配)

      题目传送门:https://www.luogu.org/problemnew/show/P2756

      这道题没什么技术含量,就是这道题(花店橱窗布置)去除了匹配权值的版本。

      对了,还要求一个匹配方案……只要看看哪几条边的流量为0(即有流流过)即可。

      代码:

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define ll long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define inf 0x3f3f3f3f
    #define mod 1000000007
    #define eps 1e-18
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=tmp*10+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll tmp=1; for(;b;b>>=1){if(b&1)tmp=tmp*a%mod; a=a*a%mod;} return tmp;}
    using namespace std;
    struct edge{
        int to,nxt,flow;
    }e[100010];
    int fir[110],lv[110],q[110];
    int n,m,S,T,tot=0;
    void add(int x,int y,int flow)
    {
        e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int dfs(int now,int flow)
    {
        if(now==T)return flow;
        for(int i=fir[now];~i;i=e[i].nxt)
            if(e[i].flow&&lv[e[i].to]==lv[now]+1){
                int tmp=dfs(e[i].to,min(flow,e[i].flow));
                e[i].flow-=tmp; e[i^1].flow+=tmp;
                if(tmp)return tmp;
            }
        return 0;
    }
    int dinic()
    {
        int i,ans=0;
        while(1){
            for(i=0;i<=T;i++)lv[i]=0;
            int h=1,t=1; q[1]=S; lv[S]=1;
            while(h<=t){
                for(i=fir[q[h]];~i;i=e[i].nxt)
                    if(e[i].flow&&!lv[e[i].to]){
                        q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1;
                    }
                ++h;
            }
            if(!lv[T])return ans;
            int k=dfs(S,inf);
            while(k)ans+=k,k=dfs(S,inf);
        }
    }
    int main()
    {
        int i;
        memset(fir,255,sizeof(fir));
        n=read(); m=read(); S=0; T=m+1;
        int x=read(),y=read();
        while(x>0&&y>0){
            add(x,y,1); x=read(); y=read();
        }
        for(i=1;i<=n;i++)add(S,i,1);
        for(i=n+1;i<=m;i++)add(i,T,1);
        printf("%d
    ",dinic());
        for(i=1;i<=n;i++)
            for(int j=fir[i];~j;j=e[j].nxt)
                if(e[j].to!=S&&!e[j].flow){
                    printf("%d %d
    ",i,e[j].to); break;
                }
    }
    洛谷P2756


     ========================================UPD:2018.5.6==================================

      4、软件补丁问题(最短路)

      题目传送门:https://www.luogu.org/problemnew/show/P2761

       这道题因为n很小(n<=20),所以我们可以把每个补丁是否被修复,这些状态压缩起来(代码中第i位=0:未修复;1:已修复),作为图中的每一个点。这样,就可以把每一个补丁看作图中的带权边,要求的就是从点0到点2n-1的最短路。

      然后我们也可以把B1、B2、F1、F2这些集合也用二进制压缩,在跑spfa时直接用位运算判断在当前状态是否能使用这个补丁,以及使用后会到达哪一个状态。

      于是就这样解决了。(不过这个数据范围真的太极限了,220*100甚至已经大于108了。。。)

      代码:

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<map>
    #define ll long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define inf 0x3f3f3f3f
    #define mod 1000000007
    #define eps 1e-18
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=tmp*10+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll tmp=1; for(;b;b>>=1){if(b&1)tmp=tmp*a%mod; a=a*a%mod;} return tmp;}
    using namespace std;
    struct data{
        int dist,id;
    };
    int que[(1<<20)+10],inq[(1<<20)+10],dist[(1<<20)+10];
    int ti[110],p[110],q[110],x[110],y[110];
    int n,m;
    char s[30];
    void spfa()
    {
        memset(dist,0x3f,sizeof(dist));
        int h=0,t=1,i;
        que[0]=0; inq[0]=1; dist[0]=0;
        while(h!=t){
            int now=que[h++]; inq[now]=0;
            if(h>1<<n)h=0;
            for(i=1;i<=m;i++)
                if(!(now&p[i])&&(now&q[i])==q[i]){
                    int nxt=(now|x[i])&(~y[i]);
                    if(dist[now]+ti[i]>=dist[nxt])continue;
                    dist[nxt]=dist[now]+ti[i];
                    if(!inq[nxt]){
                        que[t++]=nxt; inq[nxt]=1;
                        if(t>1<<n)t=0;
                    }
                }
        }
    }
    int main()
    {
        int i,j;
        n=read(); m=read();
        for(i=1;i<=m;i++){
            ti[i]=read(); p[i]=q[i]=0;
            scanf("%s",s);
            for(j=0;j<n;j++)
                if(s[j]=='+')p[i]^=1<<j;
                else if(s[j]=='-')q[i]^=1<<j;
            scanf("%s",s); x[i]=y[i]=0;
            for(j=0;j<n;j++)
                if(s[j]=='-')x[i]|=1<<j;
                else if(s[j]=='+')y[i]|=1<<j;
        }
        spfa();
        if(dist[(1<<n)-1]==inf)printf("0
    ");
        else printf("%d
    ",dist[(1<<n)-1]);
        return 0;
    }
    luoguP2761

     ========================================UPD:2018.7.1==================================

      5、太空飞行计划问题(最大权闭合子图)

      题目传送门:https://www.luogu.org/problemnew/show/P2762

      这道题是道赤裸裸的最大权闭合子图(虽然我一开始不会)。最大权闭合子图就是指在一个图的子图中,其所有结点在原图中的所有出边都只连向子图中的结点,且这个子图的点权最大。在这道题中,就是把每一个实验连若干条出边到它所需要的仪器,实验的收入赋正权,仪器的花费赋负权,这样的图的最大权闭合子图就是所求的解。

      这种问题的做法就是把原图化为二分图,源点连正权点,负权点连向汇点,流量为权值的绝对值;正权点向负权点按原图的方式连边,流量无限大。这样求出来的最大流就是最大权闭合子图的权值和。具体证明可以参考论文:胡伯涛《最小割模型在信息学竞赛中的应用》

      不过这道题还有一个难点是要输出方案。我们可以发现,源点与某正权点之间边的流量,就是这个正权点的价值被它所附带的若干个负权点消耗的值,如果它的价值被完全消耗(甚至还欠着一笔价值),那么选取这个结点是没有意义的。于是我们就可以知道,方案中要做的实验就是那些与源点的连边未满流的实验。即,若用dinic算法求最大流,在最后一次把图分层时,与源点连通的那些实验和仪器就是所需要的。

      代码:

    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<algorithm>
    #include<iostream>
    #include<queue>
    #include<vector>
    #include<map>
    #define ll long long
    #define ull unsigned long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define mod 1000000007
    #define inf 0x3f3f3f3f
    #define eps 1e-18
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll ans=0; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
    inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    using namespace std;
    struct edge{
        int to,nxt,flow;
    }e[20010];
    int fir[110],lv[110],q[110];
    int val[60],p[60];
    int n,m,S,T,tot=0;
    char tools[10000];
    void add(int x,int y,int flow)
    {
        e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int dfs(int now,int flow)
    {
        if(now==T)return flow;
        for(int i=fir[now];~i;i=e[i].nxt)
            if(e[i].flow&&lv[e[i].to]==lv[now]+1){
                int tmp=dfs(e[i].to,min(flow,e[i].flow));
                e[i].flow-=tmp; e[i^1].flow+=tmp;
                if(tmp)return tmp;
            }
        return 0;
    }
    int dinic()
    {
        int i,ans=0;
        while(1){
            for(i=0;i<=T;i++)lv[i]=0;
            int h=1,t=1; q[1]=S; lv[S]=1;
            while(h<=t){
                for(i=fir[q[h]];~i;i=e[i].nxt)
                    if(e[i].flow&&!lv[e[i].to]){
                        q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1;
                    }
                ++h;
            }
            if(!lv[T])return ans;
            int k=dfs(S,inf);
            while(k)ans+=k,k=dfs(S,inf);
        }
    }
    int main()
    {
        int i;
        memset(fir,255,sizeof(fir));
        n=read(); m=read(); S=0; T=n+m+1;
        for(i=1;i<=n;i++){
            val[i]=read();
            memset(tools,0,sizeof(tools));
            cin.getline(tools,10000);
            int ulen=0,tool;
            while (sscanf(tools+ulen,"%d",&tool)==1){
                add(i,tool+n,inf);
                if(tool==0)ulen++;
                else{
                    while(tool)tool/=10,ulen++;
                }
            }
            ulen++;
        }
        for(i=1;i<=m;i++)p[i]=read();
        for(i=1;i<=n;i++)add(S,i,val[i]);
        for(i=1;i<=m;i++)add(n+i,T,p[i]);
        int ans=0;
        for(i=1;i<=n;i++)ans+=val[i];
        ans-=dinic();
        for(i=1;i<=n;i++)if(lv[i])printf("%d ",i); printf("
    ");
        for(i=1;i<=m;i++)if(lv[i+n])printf("%d ",i); printf("
    ");
        printf("%d
    ",ans);
        return 0;
    }
    luogu2762

     ========================================UPD:2018.7.3==================================

      6、试题库问题(最大流)

      题目传送门:https://www.luogu.org/problemnew/show/P2763

       这题太傻逼了……直接看成一个有一边每个节点可以匹配k次的二分图匹配就好了。

    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<map>
    #define ll long long
    #define ull unsigned long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define mod 1000000007
    #define inf 0x3f3f3f3f
    #define eps 1e-18
    #define maxn 1010
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll ans=0; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
    inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
    using namespace std;
    struct edge{
        int to,nxt,flow;
    }e[maxn*maxn*2+4*maxn];
    int fir[2*maxn],lv[2*maxn],q[2*maxn];
    int n,m,S,T,tot=0;
    void add(int x,int y,int flow)
    {
        e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int dfs(int now,int flow)
    {
        if(now==T)return flow;
        for(int i=fir[now];~i;i=e[i].nxt)
            if(e[i].flow&&lv[e[i].to]==lv[now]+1){
                int tmp=dfs(e[i].to,min(flow,e[i].flow));
                e[i].flow-=tmp; e[i^1].flow+=tmp;
                if(tmp)return tmp;
            }
        return 0;
    }
    int dinic()
    {
        int i,ans=0;
        while(1){
            for(i=0;i<=T;i++)lv[i]=0;
            int h=1,t=1; q[1]=S; lv[S]=1;
            while(h<=t){
                for(i=fir[q[h]];~i;i=e[i].nxt)
                    if(e[i].flow&&!lv[e[i].to]){
                        q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1;
                    }
                ++h;
            }
            if(!lv[T])return ans;
            int k=dfs(S,inf);
            while(k)ans+=k,k=dfs(S,inf);
        }
    }
    int main()
    {
        memset(fir,255,sizeof(fir));
        n=read(); m=read(); S=0; T=n+m+1;
        int cnt=0;
        for(int i=1;i<=n;i++){
            int k=read(); add(S,i,k); cnt+=k;
        }
        for(int i=1;i<=m;i++){
            int p=read();
            while(p--)add(read(),n+i,1);
            add(n+i,T,1);
        }
        int ans=dinic();
        if(ans<cnt)printf("No Solution!
    ");
        else{
            for(int i=1;i<=n;i++){
                printf("%d:",i); 
                for(int j=fir[i];~j;j=e[j].nxt)
                    if(!e[j].flow&&e[j].to!=S)
                        printf("%d ",e[j].to-n);
                printf("
    ");
            } 
        }
    }
    View Code

     ========================================UPD:2018.6.1==================================

      一年之后,继续填坑。。。 

      7、最小路径覆盖问题

      题目传送门:https://www.luogu.org/problemnew/show/P2764

      有证明一个结论:DAG(有向无环图)最小路径覆盖数=结点总数-对应二分图最大匹配数。这里的对应二分图指把每个点拆成两个点,记为$B_i,W_i$,若原图中$i o j$有边,则在二分图中连一条边$B_i o W_j$。证明略。

      于是就能解决这个问题了。

    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<map>
    #define ll long long
    #define ull unsigned long long
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define lowbit(x) (x& -x)
    #define mod 1000000007
    #define inf 0x3f3f3f3f
    #define eps 1e-18
    inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;}
    inline ll power(ll a,ll b){ll ans=0; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;}
    inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;}
    using namespace std;
    struct edge{
        int to,nxt,flow;
    }e[15010];
    int fir[310],lv[310],q[310];
    int tmp[310];
    int n,m,S,T,tot=0;
    void add(int x,int y,int flow)
    {
        e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++;
        e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++;
    }
    int dfs(int now,int flow)
    {
        if(now==T)return flow;
        for(int i=fir[now];~i;i=e[i].nxt)
            if(e[i].flow&&lv[e[i].to]==lv[now]+1){
                int tmp=dfs(e[i].to,min(flow,e[i].flow));
                e[i].flow-=tmp; e[i^1].flow+=tmp;
                if(tmp)return tmp;
            }
        return 0;
    }
    int dinic()
    {
        int i,ans=0;
        while(1){
            for(i=0;i<=T;i++)lv[i]=0;
            int h=1,t=1; q[1]=S; lv[S]=1;
            while(h<=t){
                for(i=fir[q[h]];~i;i=e[i].nxt)
                    if(e[i].flow&&!lv[e[i].to]){
                        q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1;
                    }
                ++h;
            }
            if(!lv[T])return ans;
            int k=dfs(S,inf);
            while(k)ans+=k,k=dfs(S,inf);
        }
    }
    int main()
    {
        memset(fir,255,sizeof(fir));
        n=read(); m=read(); S=0; T=2*n+1;
        for(int i=1;i<=m;i++){
            int x=read(),y=read();
            add(x,y+n,1);
        }
        for(int i=1;i<=n;i++)
            add(S,i,1),add(i+n,T,1);
        int ans=dinic();
        for(int i=1;i<=n;i++)
            if(lv[i]){
                int flag=0;
                for(int j=fir[i];~j;j=e[j].nxt)
                    if(e[j].to==S&&!e[j].flow)flag=1;
                if(flag){
                    int cnt=0;
                    for(int k=i;;){
                        tmp[++cnt]=k;
                        int j=fir[k+n];
                        for(;~j;j=e[j].nxt)if(e[j].to!=T&&e[j].flow)break;
                        if(~j)k=e[j].to;
                        else break;
                    }
                    for(int j=cnt;j;j--)printf("%d ",tmp[j]);
                    printf("
    ");
                }
            }
        printf("%d
    ",n-ans);
    }
    luoguP2763

      8、魔术球问题

      题目传送门:https://www.luogu.org/problemnew/show/P2765

      我们考虑怎样的两个球可以相邻地串在同一根柱子上,显然对于这样的两个球$i,j$,满足$i<j$且$i+j$是完全平方数。那么我们考虑如何判定能否放下$x$颗球,那么只需对$[1,x]$的数按照上面的方式连边,所需的最小柱数就是该图的最小路径覆盖。显然该图是一个DAG,那么按照上面的方法求出最小路径覆盖就可以解决问题了。关于实现方法,依然可以从小到大枚举球数,然后在残量网络上新增边累加答案。


     To be continued……

  • 相关阅读:
    03-Tomcat服务器
    02-Http请求与响应全解
    01-Web客户端与服务器详解
    JavaScript高级程序设计31.pdf
    JavaScript高级程序设计30.pdf
    JavaScript高级程序设计29.pdf
    JavaScript高级程序设计28.pdf
    JavaScript高级程序设计27.pdf
    JavaScript高级程序设计26.pdf
    JavaScript高级程序设计25.pdf
  • 原文地址:https://www.cnblogs.com/quzhizhou/p/8782924.html
Copyright © 2011-2022 走看看