网络流 总结

     写了一周网络流,今天校内小测了一发,就会两道题第二题还写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\n",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\n",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\n",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\n",ans-dinic());
127    return 0;
128 }
View Code

未完待续。。。。

posted @ 2017-01-05 17:59  SD_le  阅读(234)  评论(1编辑  收藏  举报
重置按钮