zoukankan      html  css  js  c++  java
  • 网络流 总结

         写了一周网络流,今天校内小测了一发,就会两道题第二题还写GG了,真是惨2333.......

    1.最大流求最大匹配

      A.bzoj 1711 Dining吃饭

      显然每头牛要和一种食物还有一种饮料匹配,但因为要匹配两种东西,所以直接用二分图最大匹配是做不了的,那么我们可以建三层,S向食物连边,食物向牛连边,牛拆点中间连边,然后在向饮料连边,饮料向T连边。边的容量都为一,这样每条S到T的路径都是一个三种东西的匹配,容量都为一保证了不会重。

      

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<cstring>
     6 #define inf 0x3f3f3f3f
     7 #define N 505
     8 #define M 1000005
     9 using namespace std;
    10 int head[N],nxt[M],ver[M],tot,f[M];
    11 void add(int a,int b,int c)
    12 {
    13     tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;f[tot]=c;
    14     tot++;nxt[tot]=head[b];head[b]=tot;ver[tot]=a;f[tot]=0;
    15 }
    16 int S,T;
    17 int ch[N];
    18 int zeng(int a,int b)
    19 {
    20     if(a==T)return b;
    21     int r=0;
    22     for(int i=head[a];i&&b>r;i=nxt[i])
    23     {
    24         if(ch[ver[i]]==ch[a]+1&&f[i])
    25         {
    26             int t=zeng(ver[i],min(b-r,f[i]));
    27             r+=t;f[i]-=t;f[i^1]+=t;
    28         }
    29     }
    30     if(!r)ch[a]=-1;
    31     return r;
    32 }
    33 bool tell()
    34 {
    35     memset(ch,-1,sizeof(ch));
    36     ch[S]=0;
    37     queue<int>q;
    38     q.push(S);
    39     while(!q.empty())
    40     {
    41         int tmp=q.front();q.pop();
    42         for(int i=head[tmp];i;i=nxt[i])
    43         {
    44             if(f[i]&&ch[ver[i]]==-1)
    45             {
    46                 ch[ver[i]]=ch[tmp]+1;
    47                 q.push(ver[i]);
    48             }
    49         }
    50     }
    51     return ch[T]!=-1;
    52 }
    53 int dinic()
    54 {
    55     int r=0,t;
    56     while(tell())while(t=zeng(S,inf))r+=t;
    57     return r;
    58 }
    59 int n,ff,d;
    60 int main()
    61 {
    62    tot=1;
    63    scanf("%d%d%d",&n,&ff,&d);
    64    for(int i=1;i<=ff;i++)add(S,i,1);
    65    for(int i=1;i<=n;i++)add(i+ff,i+ff+n,1);
    66    S=0;T=2*n+ff+d+1;
    67    int t1,t2;
    68    for(int i=1;i<=n;i++)
    69    {
    70     scanf("%d%d",&t1,&t2);
    71     int tmp;
    72     for(int j=1;j<=t1;j++)
    73     {
    74         scanf("%d",&tmp);
    75         add(tmp,i+ff,1);
    76     }
    77     for(int j=1;j<=t2;j++)
    78     {
    79         scanf("%d",&tmp);
    80         add(i+ff+n,tmp+2*n+ff,1);
    81     }
    82    }
    83    for(int i=1;i<=d;i++)add(i+ff+2*n,T,1);
    84    printf("%d
    ",dinic());
    85    return 0;
    86 }
    View Code

      B.小测第一题

    Seal

         【题目背景】 NOI2030 前夜,作为出题人的 Z 君正在颓隔膜。这个隔膜是这样的:在上古 秘境中,勇者们发现了封印古代恶魔的地方。不幸的是,这里的封印已经濒临崩 溃,恶魔们即将苏醒。勇者们决定使用魔法水晶加固封印,让恶魔们再次沉睡。

        【题目描述】 封印恶魔的地方可以看作是一个 n*m 的矩形,包含了 n*m 个祭坛,并且其 中有一些祭坛已经损坏了。如果 i+j 为偶数,那么第 i 行第 j 列的祭坛只要没有损 坏,就一定会封印有一个恶魔。 其他的没有损坏的祭坛可以用来放置魔法水晶,但是一个祭坛上只能放置一 个魔法水晶,并且一个魔法水晶只能向一个与它相邻的祭坛输送魔力,从而加固 封印。 对于一个恶魔来说,如果与它相邻的两个成直角的水晶同时向它所在的祭坛 输送魔力的话,它就会被再次封印。 现在 Z 君想知道他最多可以封印多少恶魔?

        这道题最重要的是发现它二分图的性质,显然每一次封印魔鬼用的肯定是一个奇数列和一个偶数列的祭坛,那么就变成了和上一题一样的三元匹配。

    2.最小割解决二元关系

       一般二元关系的题有两种解法,一是在实际意义中寻找限制关系建边,二是无脑解方程组。

    A. bzoj 3996 [TJOI2015]线性代数

    Description

    给出一个N*N的矩阵B和一个1*N的矩阵C。求出一个1*N的01矩阵A.使得

    D=(A*B-C)*A^T最大。其中A^T为A的转置。输出D

       按提议中的式子列出矩阵最后的表达式,发现b[i][j]对答案有贡献当且仅当啊a[i]=1&&a[j]=1,而如果a[i]=1的话那么答案就一定会加上一个-c[i],所以从S向B中每一个点连边流量为b[i][j],b(i,j)再向新一排n个点中的i和j连边,容量inf,然后n个点向T连边权为c[i],这样Σb(i,j)-最小割即为答案。割左边代表不选i,割右边代表选i,对应付出相应代价。

      

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<queue>
     5 #include<iostream>
     6 #define N 300005
     7 #define inf 0x3f3f3f3f
     8 #define M 2000005
     9 using namespace std;
    10 int head[N],ver[M],nxt[M],f[M],tot;
    11 void add(int a,int b,int c)
    12 {
    13     tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;f[tot]=c;
    14     tot++;nxt[tot]=head[b];head[b]=tot;ver[tot]=a;f[tot]=0;
    15 }
    16 int S,T;
    17 int ch[N];
    18 int zeng(int a,int b)
    19 {
    20     if(a==T)return b;
    21     int r=0;
    22     for(int i=head[a];i&&b>r;i=nxt[i])
    23     {
    24         if(ch[ver[i]]==ch[a]+1&&f[i])
    25         {
    26             int t=zeng(ver[i],min(f[i],b-r));
    27             f[i]-=t;f[i^1]+=t;r+=t;
    28         }
    29     }
    30     if(!r)ch[a]=-1;
    31     return r;
    32 }
    33 bool tell()
    34 {
    35     memset(ch,-1,sizeof(ch));
    36     queue<int>q;q.push(S);ch[S]=0;
    37     while(!q.empty())
    38     {
    39         int tmp=q.front();q.pop();
    40         for(int i=head[tmp];i;i=nxt[i])
    41         {
    42             if(f[i]&&ch[ver[i]]==-1)
    43             {
    44                 ch[ver[i]]=ch[tmp]+1;
    45                 q.push(ver[i]);
    46             }
    47         }
    48     }
    49     return ch[T]!=-1;
    50 }
    51 int dinic()
    52 {
    53     int t,r=0;
    54     while(tell())while(t=zeng(S,inf))r+=t;
    55     return r;
    56 }
    57 int n;
    58 int main()
    59 {
    60     tot=1;
    61     scanf("%d",&n);
    62     int cnt=n;int tmp;
    63     S=0;int ans=0;
    64     for(int i=1;i<=n;i++)
    65     {
    66         for(int j=1;j<=n;j++)
    67         {
    68             scanf("%d",&tmp);
    69             cnt++;
    70             add(S,cnt,tmp);
    71             add(cnt,i,inf);
    72             add(cnt,j,tmp);
    73             ans+=tmp;
    74         }
    75     }
    76     T=cnt+1;
    77     for(int i=1;i<=n;i++)
    78     {
    79         scanf("%d",&tmp);
    80         add(i,T,tmp);
    81     }
    82     printf("%d
    ",ans-dinic());
    83     return 0;
    84 }
    View Code

      还有就是解方程连边,这样只需要n个点,但快不了多少。。。。。

    B.bzoj 2039: [2009国家集训队]employ人员雇佣 

                                                                                         Description

     作为一个富有经营头脑的富翁,小L决定从本国最优秀的经理中雇佣一些来经营自己的公司。这些经理相互之间合作有一个贡献指数,(我们用Ei,j表示i经理对j经理的了解程度),即当经理i和经理j同时被雇佣时,经理i会对经理j做出贡献,使得所赚得的利润增加Ei,j。当然,雇佣每一个经理都需要花费一定的金钱Ai,对于一些经理可能他做出的贡献不值得他的花费,那么作为一个聪明的人,小L当然不会雇佣他。 然而,那些没有被雇佣的人会被竞争对手所雇佣,这个时候那些人会对你雇佣的经理的工作造成影响,使得所赚得的利润减少Ei,j(注意:这里的Ei,j与上面的Ei,j 是同一个)。 作为一个效率优先的人,小L想雇佣一些人使得净利润最大。你可以帮助小L解决这个问题吗?

    这题就是很裸的二元关系了,但因为我实在想不出什么NB的方法所以只好去解方程了。。。

    S向每个人连容量为Ai的边,每一对人之间连2*e[i][j]容量的边,每个人i再向T连Σe(i,j)。跑就好了。

      

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<queue>
     5 #include<algorithm>
     6 #define ll long long
     7 #define inf 1233211234567LL
     8 #define N 1005
     9 #define M 15000005
    10 using namespace std;
    11 int n;
    12 int head[N],nxt[M],ver[M];
    13 ll f[M];
    14 int tot;
    15 void add(int a,int b,ll c)
    16 {
    17     tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;f[tot]=c;
    18     tot++;nxt[tot]=head[b];head[b]=tot;ver[tot]=a;f[tot]=0;
    19 }
    20 int S,T;
    21 int ch[N];
    22 ll zeng(int a,ll b)
    23 {
    24     if(a==T)return b;
    25     ll r=0;
    26     for(int i=head[a];i&&b>r;i=nxt[i])
    27     {
    28         if(ch[ver[i]]==ch[a]+1&&f[i])
    29         {
    30             ll t=zeng(ver[i],min(b-r,f[i]));
    31             r+=t;f[i]-=t;f[i^1]+=t;
    32         }
    33     }
    34     if(!r)ch[a]=-1;
    35     return r;
    36 }
    37 bool tell()
    38 {
    39     memset(ch,-1,sizeof(ch));
    40     ch[S]=0;
    41     queue<int>q;
    42     q.push(S);
    43     while(!q.empty())
    44     {
    45         int tmp=q.front();q.pop();
    46         for(int i=head[tmp];i;i=nxt[i])
    47         {
    48             if(f[i]&&ch[ver[i]]==-1)
    49             {
    50                 ch[ver[i]]=ch[tmp]+1;
    51                 q.push(ver[i]);
    52             }
    53         }
    54     }
    55     return ch[T]!=-1;
    56 }
    57 ll dinic()
    58 {
    59     ll r=0,t;
    60     while(tell())while(t=zeng(S,inf))r+=t;
    61     return r;
    62 }
    63 ll a[N];
    64 ll zong[N];
    65 signed main()
    66 {
    67     scanf("%lld",&n);
    68     tot=1;
    69     S=0;T=n+1;
    70     for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    71     for(int i=1;i<=n;i++)add(S,i,a[i]);
    72     ll tmp;ll ans=0;
    73     for(int i=1;i<=n;i++)
    74     {
    75         for(int j=1;j<=n;j++)
    76         {
    77             scanf("%lld",&tmp);
    78             ans+=tmp;
    79             if(i>j)
    80             {
    81                add(i,j,2*tmp);
    82                add(j,i,2*tmp);
    83                zong[i]+=tmp;
    84                zong[j]+=tmp;
    85             }
    86         }
    87     }
    88     for(int i=1;i<=n;i++)
    89     {
    90         add(i,T,zong[i]);
    91     }
    92     printf("%lld
    ",ans-dinic());
    93     return 0;
    94 }
    View Code

    C.bzoj 3158 千钧一发

          

         n<=1000.

        显然暴力两两枚举是否能共存。

        但如果图不是二分图(发现二分图真的好有用)的话网络流是做不了的。

         那就强行找它的性质啊,然后发现两个偶数一定满足2,两个奇数一定满足1( (2a+1)^2+(2b+1)^2,最后是2乘一个奇数的形式),所以吧数分两边中间连inf的边,向源汇连自己的值,ans=Σai-最小割。

         

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<algorithm>
      4 #include<cmath>
      5 #include<cstring>
      6 #include<queue>
      7 #define ll long long
      8 #define inf 0x3f3f3f3f
      9 #define N 1005
     10 using namespace std;
     11 int tot;
     12 int head[N],nxt[N*N+N],ver[N*N+N],f[N*N+N];
     13 void add(int a,int b,int c)
     14 {
     15     tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;f[tot]=c;
     16     tot++;nxt[tot]=head[b];head[b]=tot;ver[tot]=a;f[tot]=0;
     17 }
     18 int S,T;
     19 int ch[N];
     20 int zeng(int a,int b)
     21 {
     22     if(a==T)return b;
     23     int r=0;
     24     for(int i=head[a];i&&b>r;i=nxt[i])
     25     {
     26         if(ch[ver[i]]==ch[a]+1&&f[i])
     27         {
     28             int t=zeng(ver[i],min(b-r,f[i]));
     29             r+=t;f[i]-=t;f[i^1]+=t;
     30         }
     31     }
     32     if(!r)ch[a]=-1;
     33     return r;
     34 }
     35 bool tell()
     36 {
     37     memset(ch,-1,sizeof(ch));
     38     ch[S]=0;
     39     queue<int>q;
     40     q.push(S);
     41     while(!q.empty())
     42     {
     43         int tmp=q.front();q.pop();
     44         for(int i=head[tmp];i;i=nxt[i])
     45         {
     46             if(f[i]&&ch[ver[i]]==-1)
     47             {
     48                 ch[ver[i]]=ch[tmp]+1;
     49                 q.push(ver[i]);
     50             }
     51         }
     52     }
     53     return ch[T]!=-1;
     54 }
     55 int dinic()
     56 {
     57     int r=0,t;
     58     while(tell())while(t=zeng(S,inf))r+=t;
     59     return r;
     60 }
     61 int n;
     62 int aa[N],bb[N];
     63 int gcd(int a,int b)
     64 {
     65     if(b==0)return a;
     66     return gcd(b,a%b);
     67 }
     68 const int pp=1000003;
     69 int ha[1000005],nx[2000005],to;
     70 ll key[2000005];
     71 void insert(ll p)
     72 {
     73     ll u=p%pp;
     74     to++;ha[u]=to;nx[to]=ha[u];key[to]=u;
     75 }
     76 bool find(ll p)
     77 {
     78     ll u=p%pp;
     79     for(int i=ha[u];i;i=nx[i])
     80     {
     81         if(key[i]==p)return 1;
     82     }
     83     return 0;
     84 }
     85 int main()
     86 {
     87     tot=1;
     88    int ans=0;
     89    scanf("%d",&n);
     90    S=0;T=n+1;
     91    for(int i=1;i<=n;i++)
     92    {
     93      scanf("%d",&aa[i]);
     94    }
     95    for(int i=1;i<=n;i++)
     96    {
     97      scanf("%d",&bb[i]);
     98      ans+=bb[i];
     99    }
    100    for(int i=1;i<=n;i++)
    101    {
    102       if(aa[i]&1)
    103       {
    104         add(S,i,bb[i]);
    105       }
    106       else add(i,T,bb[i]);
    107    }
    108    for(int i=1;i<=n;i++)
    109    {
    110     if(aa[i]&1)
    111     {
    112         for(int j=1;j<=n;j++)
    113         {
    114            if(aa[j]%2==0)
    115            {
    116                ll p=(ll)aa[i]*aa[i]+(ll)aa[j]*aa[j];
    117                if(gcd(aa[i],aa[j])==1)
    118                {
    119                  ll t=sqrt(p);
    120                  if(t*t==p)add(i,j,inf);
    121                }
    122            }
    123         }
    124     }
    125    }
    126    printf("%d
    ",ans-dinic());
    127    return 0;
    128 }
    View Code

    未完待续。。。。

  • 相关阅读:
    silverlight 中缓存应用程序相应的库文件
    Silverlight 4 Unleashed 读书笔记:第二章
    使用虚拟打印机提交文档的文本
    ORACLE 中为什么要把列名都转换成大写字母?
    在 silverlight 自由绘图(WriteableBitmapEx)
    新的 WINDOWS 2003 系统上装了 TOMCAT 6 启动不了
    计算两个坐标所形成的角的角度
    在 Silverlight 绘制线帽(箭头)
    Linux下安装memecache缓存程序
    Linux下安装、配置、启动Apache
  • 原文地址:https://www.cnblogs.com/ezyzy/p/6253410.html
Copyright © 2011-2022 走看看