zoukankan      html  css  js  c++  java
  • [专题总结]网络流初步(1)

    总算A穿第一个专题。来屯思路的。

    蜥蜴

    没有比这个更板子的了。对于每个石柱拆点成两个,连边限制流量。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int cnt=2,in[22][22],out[22][22],n,m,d,tms[22][22],ecnt=1,dep[1005];
     4 int fir[1005],l[50005],to[50005],v[50005],x,maxflow,q[1005],t,cntt;
     5 int fab(int p){return p*p;}
     6 void connect(int a,int b,int vv){
     7     l[++ecnt]=fir[a];fir[a]=ecnt;to[ecnt]=b;v[ecnt]=vv;
     8     l[++ecnt]=fir[b];fir[b]=ecnt;to[ecnt]=a;
     9 }
    10 int read1(){
    11     register int ch=getchar();
    12     while(ch<'0'||ch>'3')ch=getchar();
    13     return ch-48;
    14 }
    15 int read2(){
    16     register int ch=getchar();
    17     while(ch!='L'&&ch!='.')ch=getchar();
    18     return ch=='L';
    19 }
    20 int bfs(){
    21     memset(dep,0,sizeof(dep)); dep[1]=q[1]=1; t=1;
    22     for(int h=1;h<=t;++h) for(int i=fir[q[h]];i;i=l[i])
    23         if(!dep[to[i]]&&v[i]){
    24             dep[to[i]]=dep[q[h]]+1;
    25             q[++t]=to[i];
    26             if(to[i]==2)return 1;
    27         }
    28     return 0;
    29 }
    30 int dfs(int p,int flow){
    31     if(p==2)return flow;int res=flow;
    32     for(int i=fir[p];i;i=l[i]) if(dep[to[i]]==dep[p]+1&&v[i]&&res){
    33         int q=dfs(to[i],min(res,v[i]));
    34         if(!q)dep[to[i]]=0;
    35         res-=q;v[i]-=q;v[i^1]+=q;
    36     }
    37     return flow-res;
    38 }
    39 int main(){
    40     scanf("%d%d%d",&n,&m,&d);
    41     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){
    42             tms[i][j]=read1();
    43             if(tms[i][j]) connect(cnt+1,cnt+2,tms[i][j]),
    44                 in[i][j]=++cnt,out[i][j]=++cnt;
    45         }
    46     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
    47         if(read2()) connect(1,in[i][j],1),cntt++;
    48     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(tms[i][j])
    49         for(int k=1;k<=n;++k) for(int l=1;l<=m;++l) if(tms[k][l])
    50             if(i!=k||j!=l) if(fab(i-k)+fab(j-l)<=fab(d))
    51                 connect(out[i][j],in[k][l],1234567);
    52     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(tms[i][j])
    53         if(i<=d||n-i<d||j<=d||m-j<d) connect(out[i][j],2,7654321);
    54     while(bfs())maxflow+=dfs(1,1234567);
    55     printf("%d",cntt-maxflow);
    56 }
    View Code

    星际战争

    二分答案+最大流。检查是否满流即可。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int ecnt,cnt,atk[55],def[55],can[55][55],fir[123],l[12345],to[12345];
     4 int totd,dep[123],q[123],t,n,m;
     5 double max_flow,v[12345];
     6 #define eps 1e-6
     7 void connect(int a,int b,double vv){
     8     l[++ecnt]=fir[a];fir[a]=ecnt;to[ecnt]=b;v[ecnt]=vv;
     9     l[++ecnt]=fir[b];fir[b]=ecnt;to[ecnt]=a;v[ecnt]=0;
    10 }
    11 int bfs(){
    12     memset(dep,0,sizeof(dep));dep[1]=q[1]=t=1;
    13     for(int h=1;h<=t;++h) for(int i=fir[q[h]];i;i=l[i])
    14         if(!dep[to[i]]&&v[i]>eps){
    15             q[++t]=to[i]; dep[to[i]]=dep[q[h]]+1;
    16             if(to[i]==2)return 1;
    17         }
    18     return 0;
    19 }
    20 double dfs(int p,double flow){
    21     if(p==2)return flow;double res=flow;
    22     for(int i=fir[p];i;i=l[i]) if(dep[to[i]]==dep[p]+1&&v[i]>eps&&res>eps){
    23         double succ=dfs(to[i],min(res,v[i]));
    24         if(succ<eps)dep[to[i]]=0;
    25         res-=succ;v[i]-=succ;v[i^1]+=succ;
    26     }
    27     return flow-res;
    28 }
    29 int main(){
    30     scanf("%d%d",&n,&m);
    31     for(int i=1;i<=n;++i) scanf("%d",&def[i]),totd+=def[i];
    32     for(int i=1;i<=m;++i) scanf("%d",&atk[i]);
    33     for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) scanf("%d",&can[i][j]);
    34     double l=0,r=100000,mid;
    35     while(r-l>eps){
    36         mid=(l+r)/2;ecnt=1;max_flow=0;
    37         memset(fir,0,sizeof(fir));
    38         for(int i=1;i<=m;++i) connect(1,2+i,mid*atk[i]);
    39         for(int i=1;i<=n;++i) connect(2+n+i,2,def[i]);
    40         for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) if(can[i][j]) connect(2+i,2+n+j,1e9);
    41         while(bfs())max_flow+=dfs(1,1e9);
    42         if(totd-max_flow<eps)r=mid; else l=mid;
    43     }
    44     printf("%.5lf
    ",l);
    45 }
    View Code

    网络吞吐量

    最短路。找出所有最短路上的边,然后还是拆点限制流量。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define inf 0x3f3f3f3f3f3f3f3f
     4 #define int long long
     5 priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >que;
     6 int x[505][505],n,m,fir[1234],l[555555],to[555555],v[555555],ecnt=1;
     7 int q[1234],t,dt[555],xx,max_flow,dep[1234];
     8 int _fir[1234],_l[555555],_to[555555],_v[555555],_ecnt=1;
     9 void con(int a,int b,int vv){
    10     l[++ecnt]=fir[a];fir[a]=ecnt;to[ecnt]=b;v[ecnt]=vv;
    11     l[++ecnt]=fir[b];fir[b]=ecnt;to[ecnt]=a;v[ecnt]=vv;
    12 }
    13 void _con(int a,int b,int vv){
    14     _l[++_ecnt]=_fir[a];_fir[a]=_ecnt;_to[_ecnt]=b;_v[_ecnt]=vv;
    15     _l[++_ecnt]=_fir[b];_fir[b]=_ecnt;_to[_ecnt]=a;
    16 }
    17 int bfs(){
    18     memset(dep,0,sizeof dep);t=dep[q[1]=3]=1;
    19     for(int h=1;h<=t;++h) for(int i=_fir[q[h]];i;i=_l[i]) if(_v[i]&&!dep[_to[i]]){
    20         dep[_to[i]]=dep[q[h]]+1;q[++t]=_to[i];
    21         if(_to[i]==n<<1)return 1;
    22     }    
    23     return 0;
    24 }
    25 int dfs(int p,int flow){
    26     if(p==n<<1)return flow;int res=flow;
    27     for(int i=_fir[p];i;i=_l[i])if(res&&_v[i]&&dep[_to[i]]==dep[p]+1){
    28         int q=dfs(_to[i],min(res,_v[i]));
    29         if(!q)dep[_to[i]]=0;
    30         res-=q;_v[i]-=q;_v[i^1]+=q;
    31     }
    32     return flow-res;
    33 }
    34 signed main(){
    35     scanf("%lld%lld",&n,&m);memset(x,0x3f,sizeof x);memset(dt,0x3f,sizeof dt);
    36     for(int i=1,a,b,vx;i<=m;++i)scanf("%lld%lld%lld",&a,&b,&vx),x[a][b]=x[b][a]=min(x[a][b],vx);
    37     for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) if(x[i][j]!=inf) con(i,j,x[i][j]);
    38     que.push(make_pair(0,1));dt[1]=0;
    39     while(!que.empty()){
    40         int d=que.top().first,p=que.top().second;que.pop();
    41         if(p==n)break;if(d!=dt[p])continue;
    42         for(int i=fir[p];i;i=l[i])
    43             if(dt[to[i]]>d+v[i])dt[to[i]]=d+v[i],que.push(make_pair(dt[to[i]],to[i]));
    44     }
    45     for(int i=1;i<n;++i)scanf("%lld",&xx),_con(i<<1,i<<1|1,xx);
    46     for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(dt[i]==dt[j]+x[i][j]) _con(j<<1|1,i<<1,inf);
    47     while(bfs())max_flow+=dfs(3,inf);
    48     printf("%lld
    ",max_flow);
    49 }
    View Code

    奇怪的游戏

    大型分类讨论。棋盘黑白染色。

    如果总位置数是偶数,而黑白格子之和不同,无解。

    如果黑白和一样,那么二分答案+网络流check。二分的是最后棋盘上都相同的数字是几。答案满足单调性。

    如果你能制造出一种局面使棋盘上都是x,那么因为一共偶数个格子,两两配对+1就可以让他们全是x+1。

    如果总位置数是奇数,那么如果有解,最终的值也是确定的,解方程。如一共有x个黑格子和x+1个白格子,每次操作黑白都+1。

    那么就是$sum_{black}+x==sum_{white}+x+1$。用上面二分答案里的check去看一下x这个值是否合法就行了。

    打着挺麻烦的。网络流要根据格子的黑白来分成二分图再跑。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define int long long
     4 #define inf 1234567890123450ll
     5 int n,m,T,x[42][42],tot[2],r[42][42],cnt,mx;
     6 int ecnt,fir[2222],l[12345],to[12345],v[12345],q[2222],t,dep[2222];
     7 void connect(int a,int b,int vv){//printf("%lld %lld %lld
    ",a,b,vv);
     8     l[++ecnt]=fir[a];fir[a]=ecnt;to[ecnt]=b;v[ecnt]=vv;
     9     l[++ecnt]=fir[b];fir[b]=ecnt;to[ecnt]=a;v[ecnt]=0;
    10 }
    11 int bfs(){
    12     memset(dep,0,sizeof dep);
    13     q[1]=t=dep[1]=1;
    14     for(int h=1;h<=t;++h)
    15         for(int i=fir[q[h]];i;i=l[i])
    16             if(!dep[to[i]]&&v[i]) 
    17                 dep[to[i]]=dep[q[h]]+1,q[++t]=to[i];
    18     return dep[2];
    19 }
    20 int dfs(int p,int flow){
    21     if(p==2)return flow;int res=flow;
    22     for(int i=fir[p];i;i=l[i]) 
    23         if(dep[to[i]]==dep[p]+1&&v[i]&&res){
    24             int q=dfs(to[i],min(res,v[i]));
    25             if(!q)dep[to[i]]=0;
    26             res-=q;v[i]-=q;v[i^1]+=q;
    27         }
    28     return flow-res;
    29 }
    30 bool check(int xx){
    31     memset(fir,0,sizeof fir);
    32     ecnt=1;int max_flow=0,in=0;
    33     for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
    34         if(i+j&1) connect(1,r[i][j],xx-x[i][j]),in+=xx-x[i][j];
    35         else connect(r[i][j],2,xx-x[i][j]);
    36     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(i+j&1){
    37         if(i!=1)connect(r[i][j],r[i-1][j],inf);
    38         if(i!=n)connect(r[i][j],r[i+1][j],inf);
    39         if(j!=1)connect(r[i][j],r[i][j-1],inf);
    40         if(j!=m)connect(r[i][j],r[i][j+1],inf);
    41     }
    42     while(bfs()) max_flow+=dfs(1,inf);
    43     return in==max_flow;
    44 }
    45 signed main(){
    46 //    freopen("game1.in","r",stdin);
    47     scanf("%lld",&T);
    48     while(T--){
    49         scanf("%lld%lld",&n,&m); tot[0]=tot[1]=mx=0; cnt=2;
    50         for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
    51             scanf("%lld",&x[i][j]),tot[i+j&1]+=x[i][j],mx=max(mx,x[i][j]),r[i][j]=++cnt;
    52         if(n*m&1){
    53             if(mx>tot[0]-tot[1])puts("-1");
    54             else if(check(tot[0]-tot[1])) printf("%lld
    ",(tot[0]-tot[1])*m*n-tot[0]-tot[1]>>1);
    55             else puts("-1");
    56         }else if(tot[0]!=tot[1])puts("-1");
    57         else{
    58             int l=mx,r=inf;
    59             while(l<r-1)
    60                 if(check(l+r>>1))r=l+r>>1;
    61                 else l=(l+r>>1)+1;
    62             if(check(l))printf("%lld
    ",l*n*m-tot[0]-tot[1]>>1);
    63             else printf("%lld
    ",r*n*m-tot[0]-tot[1]>>1);
    64         }
    65     }
    66 }
    View Code

    土兵占领

    我写过题解。。。

    紧急疏散evacuate

    容易想到二分时间,每一个门都限制mid的流量,加一个最短路,距离小于时间的就连边,看是否能流满。

    但是这是不对的。如果有两个人距离门都是2,而二分的时间也是2,判定为有解实际为无解。

    具体数据我忘了,大概是这个意思。WA92。

    另一个常用思想,还是拆点,不过是根据含义拆点。

    把每一个们拆成mid个。表示这扇门在时间t时逃出的机会。那么流向汇点的流量就是1。

    然后在每扇门的t到t+1连边,表示现在有人出去了的话就再等1单位时间。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 16666666
     4 const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
     5 #define tx x+dx[i]
     6 #define ty y+dy[i]
     7 int fir[S],l[S],to[S],v[S],q[S],dep[S],n,m,E,cntp,cntd,dt[444][22][22];
     8 char s[22][22];int pc,ec,nord[444][444];
     9 void DFS(int fd,int x,int y,int stp){
    10     dt[fd][x][y]=stp;
    11     for(int i=0;i<4;++i)if(dt[fd][tx][ty]>stp+1&&s[tx][ty]=='.')DFS(fd,tx,ty,stp+1);
    12 }
    13 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
    14 void con(int a,int b,int V){link(a,b,V);link(b,a,0);}
    15 bool bfs(){
    16     for(int i=1;i<=E;++i)dep[i]=0;
    17     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(!dep[to[i]]&&v[i])
    18         dep[q[++t]=to[i]]=dep[q[h]]+1;
    19     return dep[E];
    20 }
    21 int dfs(int p,int flow){int r=flow;
    22     if(p==E)return flow;
    23     for(int i=fir[p];i;i=l[i])if(dep[to[i]]==dep[p]+1&&v[i]&&r){
    24         int x=dfs(to[i],min(r,v[i]));
    25         if(!x)dep[to[i]]=0;
    26         v[i]-=x;v[i^1]+=x;r-=x;
    27     }return flow-r;
    28 }
    29 bool chk(int T){
    30     pc=0;ec=1;E=cntd*T+cntp+1;
    31     for(int i=1;i<=cntd;++i)for(int j=1;j<=T;++j)nord[i][j]=++pc,con(pc,E,1);
    32     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(s[i][j]=='.'){
    33         con(0,++pc,1);
    34         for(int k=1;k<=cntd;++k)if(dt[k][i][j]<=T)con(pc,nord[k][dt[k][i][j]],1);
    35     }
    36     for(int i=1;i<=cntd;++i)for(int j=1;j<T;++j)con(nord[i][j],nord[i][j+1],444);
    37     int maxflow=0;while(bfs())maxflow+=dfs(0,444);
    38     for(int i=0;i<=E;++i)fir[i]=0;
    39     return maxflow==cntp;
    40 }
    41 main(){dep[0]=1;
    42     scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
    43     memset(dt,0x3f,sizeof dt);
    44     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    45         if(s[i][j]=='.')cntp++;else if(s[i][j]=='D')DFS(++cntd,i,j,0);
    46     int l=0,r=n*m+1,ans=n*m+1;
    47     while(l<=r)if(chk(l+r>>1))ans=r=l+r>>1,r--;else l=(l+r>>1)+1;
    48     if(ans==n*m+1)return puts("impossible"),0;printf("%d
    ",ans);
    49 }
    View Code

    狼抓兔子

    注意是双向边。

    暴力建图跑最小割没什么问题。但是其实复杂度是不对的。

    对付这一类问题有一个特殊方法:

    适用条件:图可以花在二维平面(纸)上,而且所有边之间的交点都是原图中的结点。这样的话这种图就可以转化为对偶图。

    在满足上述条件的图里,二维平面被边分割成了若干多边形。

    假设源点S在图的左上角,汇点T在右下角。再连一条inf边从S指向T(从整个图的外围画一个大半圆),会把这个图的外部也分成两部分。简称内部和外部。

    把平面上的多边形看作新图的节点(包括被inf边隔开的“内部”与“外部”),建边,边权就是两个多边形之间的公共边的边权。

    现在我们求出一条从内部到外部的最短路,它的长度就是最小割。

    从含义上理解,你所经过的路径上的边,就是原图中要被割掉的边。要最小,那么就是最短路。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define S 1000005
     5 int fir[S],l[S*6],to[S*6],v[S*6],q[S],n,m,o[1005][1005],ec=1,pc,dep[S],ans;
     6 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     7 bool bfs(){
     8     for(int i=1;i<=pc;++i)dep[i]=0;dep[1]=q[1]=1;
     9     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(!dep[to[i]]&&v[i])
    10         dep[q[++t]=to[i]]=dep[q[h]]+1;
    11     return dep[pc];
    12 }
    13 int dfs(int p,int flow){
    14     if(p==pc)return flow;int r=flow;
    15     for(int i=fir[p];i;i=l[i])if(dep[to[i]]==dep[p]+1&&v[i]&&r){
    16         int M=dfs(to[i],min(v[i],r));
    17         if(!M)dep[to[i]]=0;
    18         r-=M;v[i]-=M;v[i^1]+=M;
    19     }return flow-r;
    20 }
    21 main(){
    22     scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)o[i][j]=++pc;
    23     for(int i=1;i<=n;++i)for(int j=1,x;j<m;++j)scanf("%d",&x),link(o[i][j],o[i][j+1],x),link(o[i][j+1],o[i][j],x);
    24     for(int i=1;i<n;++i)for(int j=1,x;j<=m;++j)scanf("%d",&x),link(o[i][j],o[i+1][j],x),link(o[i+1][j],o[i][j],x);
    25     for(int i=1;i<n;++i)for(int j=1,x;j<m;++j)scanf("%d",&x),link(o[i][j],o[i+1][j+1],x),link(o[i+1][j+1],o[i][j],x);
    26     while(bfs())ans+=dfs(1,0x3fffffff);printf("%d
    ",ans);
    27 }
    然而我没有这么写。。。在另一道题会用上的

    切糕

    自己想了八百年系列。挺难的。险些颓题解。

    最小代价,那么看着像是一个最小割了。

    对于每一个纵轴,在上面你会且仅会取一个点,为了表示这种“或”的关系。我们把所有在同一条纵轴上的点“串联”起来。

    而相邻格子是有限制的,距离不超过D。即选了(i,j,f(i,j))就必须选(i,j+1,[f(i,j)-D,f(i,j)+D])。为了表示这种“且“的关系,将其并联。

    注意到这里限制的区间是连续的一段,所以就像物理的什么导线上的试触法一样,把限制的那一段”接入电路”

    图中以“选了(1,1,3)就必须选(1,2,2~3)”为例。如果你割了v(1,1,3)这条边而不割断v(1,2,2),v(1,2,3)之一的话,那么“电流”就会绕着这条路走向终点,不满足最小割。

    所以这样就限制住了“割掉这个边,那么就必须割掉另一条纵轴上连续的区间之一”这种条件。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define rep() for(int i=1;i<=x;++i)for(int j=1;j<=y;++j)
     5 #define I 0x3fffffff
     6 int w[44][44][44],o[44][44][44],ec=1,pc,fir[66666],l[333333],to[333333],v[333333];
     7 int x,y,z,D,T,ans,q[66666],d[66666];
     8 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     9 void con(int a,int b,int V){link(a,b,V);link(b,a,0);}
    10 int up(int x){return max(x,0);}
    11 int down(int x){return min(x,z);}
    12 bool bfs(){
    13     for(int i=1;i<=T;++i)d[i]=0;
    14     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&!d[to[i]])
    15         d[q[++t]=to[i]]=d[q[h]]+1;
    16     return d[T];
    17 }
    18 int dfs(int p,int flow){
    19     if(p==T)return flow;int r=flow;
    20     for(int i=fir[p];i;i=l[i])if(v[i]&&r&&d[to[i]]==d[p]+1){
    21         int x=dfs(to[i],min(v[i],r));
    22         if(!x)d[to[i]]=0;
    23         v[i]-=x;v[i^1]+=x;r-=x;
    24     }return flow-r;
    25 }
    26 int main(){
    27     scanf("%d%d%d%d",&x,&y,&z,&D);d[0]=1;
    28     for(int k=1;k<=z;++k)rep()scanf("%d",&w[i][j][k]);
    29     rep()for(int k=0;k<=z;++k)o[i][j][k]=++pc;T=++pc;
    30     rep()con(0,o[i][j][0],I),con(o[i][j][z],T,I);
    31     rep()for(int k=1;k<=z;++k)con(o[i][j][k-1],o[i][j][k],w[i][j][k]);
    32     for(int i=1;i<x;++i)for(int j=1;j<=y;++j)for(int k=1;k<=z;++k)
    33         con(o[i][j][k-1],o[i+1][j][up(k-D-1)],I),con(o[i+1][j][down(k+D)],o[i][j][k],I);
    34     for(int i=1;i<=x;++i)for(int j=1;j<y;++j)for(int k=1;k<=z;++k)
    35         con(o[i][j][k-1],o[i][j+1][up(k-D-1)],I),con(o[i][j+1][down(k+D)],o[i][j][k],I);
    36     while(bfs())ans+=dfs(0,I);printf("%d
    ",ans);
    37 }
    View Code

    Figure Eight

    反正不是网络流。。。瞎写。。。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define e 333
     5 char c[e][e];int s[e][e],n,mxsz[e][e][e];long long ans;short u[e][e][e],d[e][e][e];
     6 bool ask(int x,int l,int r){return s[x][r]-s[x-1][r]-s[x][l-1]+s[x-1][l-1]==r-l+1;}
     7 int main(){
     8     scanf("%d",&n);
     9     for(int i=1;i<=n;++i)scanf("%s",c[i]+1);
    10     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]=='.');
    11     for(int l=1;l<=n;++l)for(int r=l+2;r<=n;++r){
    12         int lst=0;
    13         for(int i=1;i<=n;++i)
    14             if(ask(i,l,r)){if(lst)u[i][l][r]=i-lst-1;else lst=i;}
    15             else if(c[i][l]=='*'||c[i][r]=='*')lst=0;
    16     }
    17     for(int len=2;len<=n;++len)for(int l=1,r=l+len;r<=n;++l,++r)for(int i=1;i<=n;++i)
    18         mxsz[i][l][r]=max(max(mxsz[i][l+1][r],mxsz[i][l][r-1]),u[i][l][r]?(u[i][l][r]*(r-l-1)):0);
    19     for(int l=1;l<=n;++l)for(int r=l+2;r<=n;++r){
    20         int lst=0;
    21         for(int i=n;i;--i)
    22             if(ask(i,l,r)){if(lst)ans=max(ans,(lst-i-1)*(r-l-1ll)*mxsz[i][l][r]);else lst=i;}
    23             else if(c[i][l]=='*'||c[i][r]=='*')lst=0;
    24     }
    25     printf("%lld
    ",ans?ans:-1);
    26 }
    View Code

    最大获利

    最常用的最小割模型之一:选点付出一定代价,两个点同时选获得一定收益。

    网上有人管这个叫做三叉戟模型。我觉得不错。

    上来把所有可能的收益都累加答案。然后求最小割的含义就是:

    要么你同时付出A1,A2的代价(都买下来),否则你就会割舍W(1,2)的收益(没都买下来的话就不会获得收益)

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define S 3000005 
     5 #define M 100000000
     6 int fir[S],l[S],to[S],ec=1,v[S],n,m,ans,dep[S],q[S];
     7 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     8 void con(int a,int b,int V){link(a,b,V);link(b,a,0);}
     9 bool bfs(){
    10     for(int i=1;i<=n+m+1;++i)dep[i]=0;
    11     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&!dep[to[i]])
    12         dep[q[++t]=to[i]]=dep[q[h]]+1;
    13     return dep[n+m+1];
    14 }
    15 int dfs(int p,int flow){
    16     if(p==n+m+1)return flow;int r=flow;
    17     for(int i=fir[p];i;i=l[i])if(v[i]&&dep[to[i]]==dep[p]+1&&r){
    18         int x=dfs(to[i],min(r,v[i]));
    19         if(!x)dep[to[i]]=0;
    20         v[i]-=x;v[i^1]+=x;r-=x;
    21     }return flow-r;
    22 }
    23 int main(){
    24     scanf("%d%d",&n,&m);dep[0]=1;
    25     for(int i=1,x;i<=n;++i)scanf("%d",&x),con(0,i,x);
    26     for(int i=1,a,b,c;i<=m;++i)scanf("%d%d%d",&a,&b,&c),ans+=c,con(a,i+n,M),con(b,i+n,M),con(i+n,m+n+1,c);
    27     while(bfs())ans-=dfs(0,M);printf("%d
    ",ans);
    28 }
    View Code

    happiness

    两个三叉戟完事。

    “文-文-X-共理”是一个三叉戟。“理-理-Y-同文”是另一个。含义不难理解。

     1 #include<cstdio>
     2 #define S 3000005
     3 #define M 100000000
     4 int n,m,fir[S],l[S],to[S],v[S],o[101][101],pc,ec=1,ans,E,q[S],dep[S];
     5 int min(int a,int b){return a<b?a:b;}
     6 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     7 void con(int a,int b,int V){link(a,b,V);link(b,a,0);}
     8 bool bfs(){
     9     for(int i=1;i<=pc;++i)dep[i]=0;
    10     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(!dep[to[i]]&&v[i])
    11         dep[q[++t]=to[i]]=dep[q[h]]+1;
    12     return dep[E];
    13 }
    14 int dfs(int p,int flow){
    15     if(p==E)return flow;int r=flow;
    16     for(int i=fir[p];i;i=l[i])if(r&&v[i]&&dep[to[i]]==dep[p]+1){
    17         int x=dfs(to[i],min(v[i],r));
    18         if(!x)dep[to[i]]=0;
    19         v[i]-=x;v[i^1]+=x;r-=x;
    20     }return flow-r;
    21 }
    22 main(){dep[0]=1;
    23     scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)o[i][j]=++pc;E=++pc;
    24     for(int i=1;i<=n;++i)for(int j=1,x;j<=m;++j)scanf("%d",&x),con(o[i][j],E,x),ans+=x;
    25     for(int i=1;i<=n;++i)for(int j=1,x;j<=m;++j)scanf("%d",&x),con(0,o[i][j],x),ans+=x;
    26     for(int i=1;i<n;++i)for(int j=1,x;j<=m;++j)scanf("%d",&x),con(o[i][j],++pc,M),con(o[i+1][j],pc,M),con(pc,E,x),ans+=x;
    27     for(int i=1;i<n;++i)for(int j=1,x;j<=m;++j)scanf("%d",&x),con(++pc,o[i][j],M),con(pc,o[i+1][j],M),con(0,pc,x),ans+=x;
    28     for(int i=1;i<=n;++i)for(int j=1,x;j<m;++j)scanf("%d",&x),con(o[i][j],++pc,M),con(o[i][j+1],pc,M),con(pc,E,x),ans+=x;
    29     for(int i=1;i<=n;++i)for(int j=1,x;j<m;++j)scanf("%d",&x),con(++pc,o[i][j],M),con(pc,o[i][j+1],M),con(0,pc,x),ans+=x;
    30     while(bfs())ans-=dfs(0,M);printf("%d
    ",ans);
    31 }
    View Code

    employ人员雇佣

    两人都雇+2w(i,j),雇一个-w(i,j),一个都不雇+0。建三叉戟。

    可以认为:雇一个人需要额外付出w(i,j),而两个人同时雇会获得4w(i,j)。

    怎么想到?解方程啊!

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define int long long
     4 #define inf 12345678901234567ll
     5 int fir[2000005],l[12000005],to[12000005],v[12000005],ecnt=1,cnt,ans;
     6 int q[2000005],dep[2000005],n,t,cost[1005];
     7 void link(int a,int b,int vv){l[++ecnt]=fir[a];to[ecnt]=b;fir[a]=ecnt;v[ecnt]=vv;}
     8 int bfs(){
     9     memset(dep,0,sizeof dep);dep[q[1]=t=1]=1;
    10     for(int h=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&!dep[to[i]])
    11         dep[q[++t]=to[i]]=dep[q[h]]+1;
    12     return dep[2];
    13 }
    14 int dfs(int p,int flow){
    15     int res=flow;if(p==2)return flow;
    16     for(int i=fir[p];i;i=l[i])if(dep[to[i]]==dep[p]+1&&v[i]&&res){
    17         int q=dfs(to[i],min(v[i],res));
    18         if(!q)dep[to[i]]=0;
    19         v[i]-=q;res-=q;v[i^1]+=q;
    20     }
    21     return flow-res;
    22 }
    23 signed main(){
    24     scanf("%lld",&n);cnt=2+n;
    25     for(int i=1;i<=n;++i)scanf("%lld",&cost[i]);
    26     for(int i=1,w;i<=n;++i)for(int j=1;j<=n;++j){
    27         scanf("%lld",&w);cost[i]+=w;
    28         if(i<j)ans+=w<<2,
    29         link(2+i,++cnt,inf),link(cnt,2+i,0),
    30         link(2+j,cnt,inf),link(cnt,2+j,0),
    31         link(cnt,2,w<<2),link(2,cnt,0);
    32     }
    33     for(int i=1;i<=n;++i)link(1,2+i,cost[i]),link(2+i,1,0);//printf("%lld
    ",ans);
    34     while(bfs())ans-=dfs(1,inf);
    35     printf("%lld
    ",ans);
    36 }
    它T掉了

    关键在于对于每一对关系都新建了一个点,这样点的数量是$O(n^2)$的。

    如果通过直接在两个点之间连边而不是三叉戟的话,就可以降为$O(n)$

    解决方法是,雇佣每个人的代价还是$c_i+sumlimits_{i=1}^{n} w(i,j)$

    然后在两个人之间建边,即i向j连$2w(i,j)$。

    当然还可以通过QJ数据A掉这道题,只要你不把w为0的边建出来就能没脸AC了。

     1 #include<cstdio>
     2 #define S 5000005
     3 #define M 0x3ffffffffffff
     4 #define int long long
     5 int min(int a,int b){return a<b?a:b;}
     6 int n,c[1001],w[1001][1001],fir[S],l[S],to[S],v[S],ec=1,pc,q[S],dep[S],ans,E;
     7 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     8 void con(int a,int b,int V){link(a,b,V);link(b,a,0);}
     9 bool bfs(){
    10     for(int i=1;i<=pc;++i)dep[i]=0;
    11     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(!dep[to[i]]&&v[i])
    12         dep[q[++t]=to[i]]=dep[q[h]]+1;
    13     return dep[E];
    14 }
    15 int dfs(int p,int flow){
    16     if(p==E)return flow;int r=flow;
    17     for(int i=fir[p];i;i=l[i])if(dep[to[i]]==dep[p]+1&&v[i]&&r){
    18         int x=dfs(to[i],min(r,v[i]));
    19         if(!x)dep[to[i]]=0;
    20         v[i]-=x;v[i^1]+=x;r-=x;
    21     }return flow-r;
    22 }
    23 main(){
    24     scanf("%lld",&n);pc=E=n+1;dep[0]=1;
    25     for(int i=1;i<=n;++i)scanf("%lld",&c[i]);
    26     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%lld",&w[i][j]);
    27     for(int i=1;i<=n;++i)for(int j=1+i;j<=n;++j)if(w[i][j])
    28         con(++pc,E,w[i][j]<<2),con(i,pc,M),con(j,pc,M),ans+=w[i][j]<<2,c[i]+=w[i][j],c[j]+=w[i][j];
    29     for(int i=1;i<=n;++i)con(0,i,c[i]);
    30     while(bfs())ans-=dfs(0,M);printf("%lld
    ",ans);
    31 }
    View Code

    不同的最小割

    最小割树的板子。

    任选源汇求出最小割x,这个最小割会把集合划分为两个部分S与T。S内的点到T的点的最小割会对x取min。

    再对两个集合分治下去,直到集合大小为1。过程中得到的所有最小割就是答案。

    这样就只用做$O(n)$次网络流了。

    至于怎么求出集合:就是从源点开始dfs,只走没有满流的边(即v还有值的边)所到达的点都属于S,没有dfs到的属于T。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<set>
     4 using namespace std;
     5 set<int>ans;
     6 int n,m,fir[888],l[18888],to[18888],v[18888],d[888],q[888],ec=1,S,E,cnt,a[888],al[888],t[888];
     7 void link(int a,int b,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;}
     8 bool bfs(){
     9     for(int i=1;i<=n;++i)d[i]=0;q[d[S]=1]=S;
    10     for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&!d[to[i]])
    11         d[q[++t]=to[i]]=d[q[h]]+1;
    12     return d[E];
    13 }
    14 int dfs(int p,int flow){int r=flow;
    15     if(p==E)return flow;
    16     for(int i=fir[p];i&&r;i=l[i])if(d[to[i]]==d[p]+1&&v[i]){
    17         int x=dfs(to[i],min(v[i],r));
    18         if(!x)d[to[i]]=0;
    19         v[i]-=x;v[i^1]+=x;r-=x;
    20     }return flow-r;
    21 }
    22 void DFS(int p){
    23     al[p]=1;
    24     for(int i=fir[p];i;i=l[i])if(v[i]&&!al[to[i]])DFS(to[i]);
    25 }
    26 int dinic(int x=0){while(bfs())x+=dfs(S,0x3fffffff);return x;}
    27 void Divide(int l,int r){
    28     if(l>=r)return;
    29     for(int i=2;i<=ec;i+=2)v[i]=v[i^1]=v[i]+v[i^1]>>1;
    30     S=a[l];E=a[r];ans.insert(dinic());DFS(S);int L=l-1,R=r+1;
    31     for(int i=l;i<=r;++i)if(al[a[i]])t[++L]=a[i];else t[--R]=a[i];
    32     for(int i=1;i<=n;++i)al[i]=0;
    33     for(int i=l;i<=r;++i)a[i]=t[i];
    34     Divide(l,L);Divide(R,r);
    35 }
    36 int main(){
    37     scanf("%d%d",&n,&m);
    38     for(int i=1,x,y,V;i<=m;++i)scanf("%d%d%d",&x,&y,&V),link(x,y,V),link(y,x,V);
    39     for(int i=1;i<=n;++i)a[i]=i;
    40     Divide(1,n);printf("%u
    ",ans.size());
    41 }
    View Code

    晨跑

    最小费用最大流板子。要注意对于从1到n的直接连边只能走1次。所以边权(流量)不能是inf。

     1 #include<cstdio>
     2 int n,m,fir[444],l[44444],to[44444],pre[444],v[44444],w[44444],q[44444],d[444],iq[444],ec=1;
     3 int day,ans;
     4 void link(int a,int b,int V,int W){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=V;w[ec]=W;}
     5 bool SPFA(){
     6     for(int i=1;i<=(n<<1|1);++i)d[i]=0x3fffffff;d[q[1]=3]=0;
     7     for(int h=1,t=1;h<=t;iq[q[h]]=0,++h)for(int i=fir[q[h]];i;i=l[i])if(d[to[i]]>d[q[h]]+w[i]&&v[i]){
     8         pre[to[i]]=i;d[to[i]]=d[q[h]]+w[i];
     9         if(!iq[to[i]])iq[q[++t]=to[i]]=1;
    10     }return d[n<<1]<0x3fffffff;
    11 }
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1,x,y,W;i<=m;++i)scanf("%d%d%d",&x,&y,&W),link(x<<1|1,y<<1,1,W),link(y<<1,x<<1|1,0,-W);
    15     for(int i=1;i<=n;++i)link(i<<1,i<<1|1,1,0),link(i<<1|1,i<<1,0,0);
    16     while(SPFA()){day++;for(int i=pre[n<<1];i;i=pre[to[i^1]])ans+=w[i],v[i]--,v[i^1]++;}
    17     printf("%d %d
    ",day,ans);
    18 }
    View Code

    80人环游世界

    上下届无源汇最小费用可行流板子。

    啊fixed by LNC:我写的是有源汇的但是我忘记为什么了

     1 #include<cstdio>
     2 int Ss=0,s=1,t=2,St=3,cn=3,n,m,dt[101][101],req[101],P[101][2];
     3 int fir[333],l[666666],to[666666],v[666666],c[666666],cnt=1,ans;
     4 int pre[333],dep[333],q[66666],iq[333];
     5 void link(int a,int b,int w,int C){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=w;c[cnt]=C;}
     6 bool SPFA(){
     7     for(int i=1;i<=cn;++i)dep[i]=1234567890;
     8     for(int h=1,t=1;h<=t;++h,iq[q[h]]=0)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&dep[to[i]]>dep[q[h]]+c[i]){
     9         dep[to[i]]=dep[q[h]]+c[i];pre[to[i]]=i;
    10         if(!iq[to[i]])q[++t]=to[i],iq[to[i]]=1;
    11     }
    12     return dep[St]!=1234567890;
    13 }
    14 int main(){
    15     scanf("%d%d",&n,&m);
    16     for(int i=1;i<=n;++i)scanf("%d",&req[i]);
    17     for(int i=1;i<n;++i)for(int j=i+1;j<=n;++j)scanf("%d",&dt[i][j]);
    18     for(int i=1;i<=n;++i)P[i][0]=++cn,P[i][1]=++cn;
    19     link(Ss,s,m,0);link(s,Ss,0,0);
    20     link(t,St,m,0);link(St,t,0,0);
    21     for(int i=1;i<=n;++i)link(s,P[i][0],1234567890,0),link(P[i][0],s,0,0);
    22     for(int i=1;i<=n;++i)link(P[i][1],t,1234567890,0),link(t,P[i][1],0,0);
    23     for(int i=1;i<=n;++i)link(Ss,P[i][1],req[i],0),link(P[i][1],Ss,0,0);
    24     for(int i=1;i<=n;++i)link(P[i][0],St,req[i],0),link(St,P[i][0],0,0);
    25     for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)if(dt[i][j]!=-1)
    26         link(P[i][1],P[j][0],1234567890,dt[i][j]),link(P[j][0],P[i][1],0,-dt[i][j]);
    27     while(SPFA())for(int i=pre[St];i;i=pre[to[i^1]])v[i]--,v[i^1]++,ans+=c[i];
    28     printf("%d
    ",ans);
    29 }
    View Code

    修车

    正难则反。还是拆点。把一个修车师傅拆成n个点,其中点d(i,j)表示第i个修车师傅修的倒数第j辆车。对于一辆时间为t的车,连向这个点的边权为t*j。

    因为在它后面的所有车都需要等待t时间。因为它是倒数第j个,所以它后面有(j-1)个,算上他自己有j个,每个人都等t,所以是t×j。

     1 #include<cstdio>
     2 int n,m,t[666][100],num[100][666],cn,fir[6666],l[222222],to[222222],v[222222],c[222222],ans,cnt=1;
     3 int iq[6666],q[222222],dt[6666],pre[6666];
     4 void con(int a,int b,int w,int C){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=w;c[cnt]=C;}
     5 void link(int a,int b,int C){con(a,b,1,C);con(b,a,0,-C);}
     6 bool SPFA(){
     7     for(int i=1;i<=cn;++i)dt[i]=1234567890;
     8     for(int h=1,t=1;h<=t;++h,iq[q[h]]=0)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&dt[q[h]]+c[i]<dt[to[i]]){
     9         dt[to[i]]=dt[q[h]]+c[i];pre[to[i]]=i;
    10         if(!iq[to[i]])iq[to[i]]=1,q[++t]=to[i];
    11     }
    12     return dt[cn]!=1234567890;
    13 }
    14 int main(){
    15     scanf("%d%d",&m,&n);cn=n;
    16     for(int i=1;i<=n;++i)link(0,i,0);
    17     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)num[j][i]=++cn,scanf("%d",&t[i][j]);
    18     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=1;k<=n;++k)link(i,num[j][k],t[i][j]*k);
    19     cn++;
    20     for(int j=1;j<=m;++j)for(int k=1;k<=n;++k)link(num[j][k],cn,0);
    21     while(SPFA())for(int i=pre[cn];i;i=pre[to[i^1]])ans+=c[i],v[i]--,v[i^1]++;
    22     printf("%.2lf
    ",ans*1.0/n);
    23 }
    View Code

    数字配对

    带权匹配问题。。。二分图才可做啊。

    可以根据每个数分解质因数后质因子个数是奇偶数来分部。当然同部点不会连边,因为质数个数差为偶数的话就算能整除,商也是至少有2个质因子,是合数不连边。

    但是我没有发现,我yy了一种联动边权。网络流在流经边A时也会使B的流量等量减少。

    这样的话把每个点放在对偶图两边,一边消耗是另一边也消耗。

    在不是二分图时会被hack。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define M 10000005
     5 #define E 2*n+1
     6 int a[202],b[202],n,fir[405],l[M],to[M],v[M],ec=3,ans,iq[405],q[M],pre[M];
     7 long long w[M],dt[405],c[202],C;
     8 void link(int a,int b,long long W,int V){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=W;v[ec]=V;}
     9 bool isprime(int x){
    10     if(x==1)return 0;
    11     for(int i=2;i*i<=x;++i)if(x%i==0)return 0;
    12     return 1;
    13 }
    14 bool SPFA(){
    15     for(int i=1;i<=E;++i)dt[i]=0x3ffffffffffff;
    16     for(int h=1,t=1;h<=t;iq[q[h]]=0,++h)for(int i=fir[q[h]];i;i=l[i])
    17         if(v[i]&&dt[to[i]]>dt[q[h]]+w[i]){
    18             dt[to[i]]=dt[q[h]]+w[i],pre[to[i]]=i;
    19             if(!iq[to[i]])iq[q[++t]=to[i]]=1;
    20         }
    21     return dt[E]<0x3ffffffffffff;
    22 }
    23 main(){scanf("%d",&n);
    24     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    25     for(int i=1;i<=n;++i)scanf("%d",&b[i]);
    26     for(int i=1;i<=n;++i)scanf("%lld",&c[i]);
    27     for(int i=1;i<=n;++i)link(0,i,0,b[i]),link(i,0,0,0),link(i+n,E,0,b[i]),link(E,i+n,0,0);
    28     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)if(a[i]%a[j]==0&&isprime(a[i]/a[j]))
    29         link(i,j+n,-c[i]*c[j],M),link(j+n,i,c[i]*c[j],0),link(j,i+n,-c[i]*c[j],M),link(i+n,j,c[i]*c[j],0);
    30     while(SPFA()){
    31         int x=M;long long c=0;
    32         for(int i=pre[E];i;i=pre[to[i^1]])x=min(x,v[i]),c+=w[i];
    33         if(C>=x*c){ans+=x,C-=x*c;for(int i=pre[E];i;i=pre[to[i^1]])v[i]-=x,v[i^1]+=x,v[i^2]-=x,v[i^3]+=x;}
    34         else{ans+=C/c;break;}
    35     }printf("%d
    ",ans);
    36 }
    View Code

    美食节

    和《修车》一样。但是会T。

    在跑增广路的时候,动态开点。不要都开出来,就可以了。

     1 #include<cstdio>
     2 int n,m,x[450],num[1005][8005],t[450][8005],cn,ans,tot;
     3 int fir[222222],l[20000005],to[20000005],c[20000005],v[20000005],tt[20000005],cnt=1;
     4 int iq[222222],q[222222],dt[222222],pre[222222],al[1005][8005];
     5 void link(int a,int b,int w,int C,int T){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;v[cnt]=w;c[cnt]=C;tt[cnt]=T;}
     6 bool SPFA(){
     7     for(int i=1;i<=cn;++i)dt[i]=1234567890;
     8     for(int h=1,t=1;h<=t;++h,iq[q[h]]=0)for(int i=fir[q[h]];i;i=l[i])if(dt[to[i]]>dt[q[h]]+c[i]&&v[i]){
     9         dt[to[i]]=dt[q[h]]+c[i];pre[to[i]]=i;
    10         if(!iq[to[i]])q[++t]=to[i],iq[to[i]]=1;
    11     }
    12     return dt[cn]!=1234567890;
    13 }
    14 int main(){
    15     scanf("%d%d",&n,&m);cn=n;
    16     for(int i=1;i<=n;++i)scanf("%d",&x[i]),link(0,i,x[i],0,0),link(i,0,0,0,0),tot+=x[i];
    17     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&t[i][j]);
    18     for(int j=1;j<=m;++j)for(int k=1;k<=tot;++k)num[j][k]=++cn;
    19     cn++;
    20     for(int j=1;j<=m;++j)for(int k=1;k<=tot;++k)link(num[j][k],cn,1,0,0),link(cn,num[j][k],0,0,0);
    21     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=1;k<=1;++k)
    22         link(i,num[j][k],1,t[i][j]*k,1),link(num[j][k],i,0,-t[i][j]*k,1);
    23     while(SPFA()){
    24         for(int i=pre[cn];i;i=pre[to[i^1]]){
    25             v[i]--,v[i^1]++,ans+=c[i];
    26             int x=to[i],y=to[i^1],a;
    27             if(!tt[i])continue;
    28             if(x<y)continue;else x^=y^=x^=y;
    29             for(int j=1;j<=m;++j)if(num[j][tt[i]]==y)a=j;
    30             if(!al[a][tt[i]+1])for(int j=1;j<=n;++j)link(j,num[a][tt[i]+1],1,t[j][a]*(tt[i]+1),tt[i]+1),link(num[a][tt[i]+1],j,0,-t[j][a]*(tt[i]+1),tt[i]+1);
    31             al[a][tt[i]+1]=1;
    32         }
    33     }
    34     printf("%d
    ",ans);
    35 }
    View Code
  • 相关阅读:
    C#面向对象(二)之抽象类实现多态
    JavaWeb 学习0010-今日问题 2016-12-3
    JavaWeb 学习008-今日问题(非空验证尚未解决) 2016-12-2
    JavaWeb 学习007-4个页面,5条sql语句(添加、查看、修改、删除)2016-12-2
    JavaWeb 学习006-4个页面,5条sql语句(添加、查看、修改、删除)
    JavaWeb 学习005-4个页面,5条sql语句(添加、查看、修改、删除)
    JavaWeb 学习004-增删改查的编写
    JavaWeb 学习003-简单登录页面功能实现
    JavaWeb 学习001-登录页面-Servlet
    JavaWeb 学习001-登录页面
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11960518.html
Copyright © 2011-2022 走看看