网络流学习记录

为了准备省选(这话似乎说了很多次了。。。),机房学习了网络流的算法,权衡两个算法,我最终学习了简单好写的ISAP

ISAP的基本思路是利用流量平衡原理,找一条从源点到汇点的增广路径,反向弧加上这条路径上的最小剩余流量,正向弧减去,如果找不到,则调整距离标号。ISAP(大多数都把ISAP当作SAP)网上的资源非常多,这里不再赘述。给出一个非常好的讲ISAP链接:http://www.cnblogs.com/wally/archive/2013/05/03/3054778.html

题目COGS 885 草地排水 (裸题) code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int map[201][201],n,m;
int pre[201],gap[201],lev[201];
int ISAP(int vs,int vt)
{
	memset(pre,-1,sizeof(pre));
	memset(gap,0,sizeof(gap));
	memset(lev,0,sizeof(lev));
	int minl,maxt=0,i,v,u=pre[vs]=vs,aug=2100000000;
	maxt=0; gap[0]=vt;
	while (lev[vs]<vt)
	  {
	    for (v=1;v<=vt;v++)
	      if (lev[u]==lev[v]+1&&map[u][v]>0)
	        break;
	    if (v<=vt)
	      {  
	        pre[v]=u;
		    u=v; 
		    if (v==vt)
		      {
		        aug=2100000000;
		        for (i=v;i!=vs;i=pre[i])
		          if (aug>map[pre[i]][i])
		            aug=map[pre[i]][i];
		        maxt+=aug;
		        for (i=v;i!=vs;i=pre[i])
		          {
		            map[pre[i]][i]-=aug;
		            map[i][pre[i]]+=aug;
		          }
		        u=vs;
		      }
	      }
	    else
	      {
	        minl=vt;
		    for (v=1;v<=vt;++v)
		      if (map[u][v]>0&&minl>lev[v])
		        minl=lev[v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        gap[lev[u]]++;
            u=pre[u];
          }
      }
    return maxt;
}
int main()
{
	int i,j,u,v,c;
	freopen("ditch.in","r",stdin);
	freopen("ditch.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;++i)
	  {
	    scanf("%d%d%d",&u,&v,&c);
		map[u][v]+=c;  
	  }
	printf("%d\n",ISAP(1,m));
	fclose(stdin); fclose(stdout);
}

COGS 11 运输问题1 有源有汇有上界无下界最大流 code:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int map[101][101],n;
int lev[101],gap[101],pre[101];
int ISAP(int vs,int vt)
{
	memset(gap,0,sizeof(gap));
	memset(lev,0,sizeof(lev));
	memset(pre,-1,sizeof(pre));
	int i,minl,u=pre[vs]=vs,v,aug,maxt=0;
	gap[0]=vt;
	while (lev[vs]<vt)
	  {
	    for (v=1;v<=vt;v++)
	      if (lev[u]==lev[v]+1&&map[u][v]>0)
	        break;
	    if (v<=vt)
	      {
	      	pre[v]=u; 
			u=v;
	        if (v==vt)
	          {
	            aug=2100000000;
				for (i=v;i!=vs;i=pre[i])
				  if (map[pre[i]][i]<aug) 
				    aug=map[pre[i]][i];
				maxt+=aug;
				for (i=v;i!=vs;i=pre[i])
				  {
				    map[pre[i]][i]-=aug;
				    map[i][pre[i]]+=aug;
				  }
				u=vs; 
	          }
	      }
	    else
	      {
	        minl=vt;
	        for (v=1;v<=vt;++v)
	          if (map[u][v]>0&&minl>lev[v])
	            minl=lev[v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        gap[lev[u]]++;
	        u=pre[u];
	      }
	  }
	return maxt;
}
int main()
{
	int i,j;
	freopen("maxflowa.in","r",stdin);
	freopen("maxflowa.out","w",stdout);
	scanf("%d\n",&n);
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    scanf("%d",&map[i][j]);
	printf("%d\n",ISAP(1,n));
	fclose(stdin);
	fclose(stdout);
}

COGS 12 运输问题2  有源有汇有上界有下界最大流 code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int map[150][150],n;
int lev[150],pre[150],gap[150];
int ISAP(int vs,int vt)
{
	memset(lev,0,sizeof(lev));
	memset(gap,0,sizeof(gap));
	memset(pre,-1,sizeof(pre));
	int i,v,u=pre[vs]=vs,minl,aug,maxt=0;
	gap[0]=vt-vs+1;
	while (lev[vs]<vt)
	  {
	    for (v=vs;v<=vt;v++)
	      if (map[u][v]>0&&lev[u]==lev[v]+1)
	        break;
	    if (v<=vt)
	      {
	        pre[v]=u;
	        u=v;
	        if (v==vt)
	          {
	            aug=2100000000;
	            for (i=v;i!=vs;i=pre[i])
	              if (map[pre[i]][i]<aug)
	                aug=map[pre[i]][i];
	            maxt+=aug;
	            for (i=v;i!=vs;i=pre[i])
	              {
	                map[pre[i]][i]-=aug;
	                map[i][pre[i]]+=aug;
	              }
	            u=vs;
	          }
	      }
	    else
	      {
	        minl=vt;
	        for (v=vs;v<=vt;++v)
	          if (map[u][v]>0&&lev[v]<minl)
	            minl=lev[v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        gap[lev[u]]++;
	        u=pre[u];
	      }
	  }
	return maxt;
}
int main()
{
	int i,j,m,inf,outf,sum,ans,p=0;
	int pic[150][150][2];
	freopen("maxflowb.in","r",stdin);
	freopen("maxflowb.out","w",stdout) 
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	  for (j=1;j<=2*n;++j)
	    scanf("%d",&pic[i][(j+1)/2][1-j%2]);
	for (i=1;i<=n;++i)
	  {
	  	inf=0; outf=0;
	    for (j=1;j<=n;++j)
	      {
		    inf+=pic[j][i][0];
		    outf+=pic[i][j][0];
		    map[i][j]=pic[i][j][1]-pic[i][j][0];
		  }
		sum=outf-inf;
		if (sum<0)
		  {
		    map[0][i]=abs(sum);
		    p+=abs(sum);
		  }
		else
		  map[i][n+1]=abs(sum);
      }
    map[n][1]=2100000000;
    ans=ISAP(0,n+1);
    if (ans==p)
      {
      	map[n][1]=0;
      	ans=ISAP(1,n);
        printf("%d\n",ans);
      }
    else
      printf("0\n"); 
}

COGS 13  运输问题4 最小费用最大流模板题 code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int point[101],next[5000],queue[500],dis[101],minn[101],pre[101],e,head,tail,ans=0,n,st,en;
bool exist[101];
struct hp{
	int u,v,c,w;
}a[5000];
void add(int u,int v,int c,int w)
{
	e++;
	a[e].u=u; a[e].v=v; a[e].c=c; a[e].w=w; next[e]=point[u]; point[u]=e;
	e++;
	a[e].u=v; a[e].v=u; a[e].c=0; a[e].w=0-w; next[e]=point[v]; point[v]=e;
}
bool work(int vs,int vt)
{
	memset(exist,false,sizeof(exist));
	memset(dis,127,sizeof(dis));
	memset(pre,0,sizeof(pre));
    int i,j,u,stan=dis[1]; minn[vs]=dis[1];
    exist[vs]=true; dis[vs]=0; head=0; tail=1; queue[tail]=vs;
	while (head!=tail)
	  {
	    head=head%500+1;
		u=queue[head];
		exist[u]=false;
		for (i=point[u];i!=0;i=next[i])
		  {
		    if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
		      {
		        dis[a[i].v]=dis[u]+a[i].w;
		        pre[a[i].v]=i;
		        minn[a[i].v]=min(minn[u],a[i].c);
		        if (!exist[a[i].v])
		          {
		            tail=tail%500+1;
		            queue[tail]=a[i].v;
		            exist[a[i].v]=true;
		          }
		      }
		  }  
	  }
	if (dis[vt]==stan) return false;
	ans+=dis[vt]*minn[vt];
	for (i=pre[vt];i!=0;i=pre[a[i].u])
	  {
	    a[i].c-=minn[vt];
	    a[i^1].c+=minn[vt];
	  }
	return true;
}
int main()
{
	int i,j,c,w;
	scanf("%d%d%d",&n,&st,&en);
	e=1;
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    {
	      scanf("%d%d",&c,&w);
	      if (c) add(i,j,c,w);
	    }  
	while (work(st,en));
	printf("%d\n",ans);
}


寒假网上授课 Day1 测试题1 肉鸡翅(貌似是出题人自己出的,下面给出题目)

1. 肉鸡翅

要做出绝顶美味让人大快朵颐的肉鸡翅,需要放入很多美味的调料。

此时麦当劳来了一位大客户,他要n*m个鸡翅,并且要求用一个带铁丝网格的巨大nm列铁板装着鸡翅(每个格子里装一个),每个格子里的鸡翅必须用此客户指定的调料(不尽相同),然后此客户将这个铁板整个带回家当艺术品享用。

铁板被分成n*m 个方格,每一个格子都只能放上带指定调料的鸡翅,调料是不能覆盖的,即假如P格要放x味的调料,那么不能先放上y味的调料再放上x味的调料。

麦当劳首席大厨TYG很崇尚艺术,他买了一台自动洒调料机,有三种洒调料方法:

A. 将同一行某些连续的格子洒上同一种味道的调料,机器损耗的代价为

B. 将同一列某些连续的格子洒上同一种味道的调料,机器损耗的代价为

C. 将某一个格子撒上某种味的调料,机器损耗的代价为

现在TYG想要一种代价最少的方案将这些鸡翅按客户指定的要求撒上调料。

 

【输入】:meatchickwing.in

本题有多组数据(有很多那种大客户来订单,引进肉鸡品牌后销量明显增了很多),首先第一行输入数据组数,

每组数据的输入如下:

第一行五个整数n,m,a,b,c 

接下来一个n*m 的矩阵,表示每一个网格中应洒调料的种类,调料种类用小写字母表示。

(注:不保证字母是连续的出现的。也就是说可能只出现了ac两种字母)

 

【输出】:meatchickwing.out

对于每组数据,输出一个整数ans,表示最小代价

样例输入:

3 4 11 8 3 

aaaa

aaaa

aaaa

4 4 3 5 2 

aabc

aabc

bbbb

ccbc

样例输出:

32 

23 

数据范围:

数据组数不超过 40

1<=n,m<=30  

1<=a,b,c<=100000


终于见到了网络流真正的难点,与差分约束一样,都难在建模上,我们统计一下横的相同的块数toth,纵的相同的块数totl,从s引向toth个点一条上限为a的边,从totl个点引向t一条上限为b的边,对于每个相交的“横块”和“纵块”,在他们之间引一条长度为c的边。跑一遍最大流即可。

理解的姿势是这样的:首先,假设点(i,j),那么他一定同时属于一个“横块”a和”纵块“b,s->a->b->t一定会有一条边是在最小割中,再根据最小割等于最大流的原理,就可以求出最小费用 code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int map[2000][2000],n,m,a,b,c;
int pre[2000],lev[2000],gap[2000];
struct hp{
	int x,y;
}dot[31][31];
int ISAP(int vs,int vt)
{
	memset(pre,-1,sizeof(pre));
	memset(lev,0,sizeof(lev));
	memset(gap,0,sizeof(gap));
	int i,v,u=pre[vs]=vs,minl,maxt=0,aug;
	gap[0]=vt-vs+1;
	while (lev[vs]<vt)
	  {
	    for (v=vs;v<=vt;v++)
	      if (map[u][v]>0&&lev[u]==lev[v]+1)
	        break;
	    if (v<=vt)
	      {
	        pre[v]=u;
	        u=v;
	        if (v==vt)
	          {
	            aug=2100000000;
	            for (i=v;i!=vs;i=pre[i])
	              if (aug>map[pre[i]][i])
	                aug=map[pre[i]][i];
	            maxt+=aug;
	            for (i=v;i!=vs;i=pre[i])
	              {
	                map[pre[i]][i]-=aug;
	                map[i][pre[i]]+=aug;
	              }
	            u=vs;
	          }
	      }
	    else
	      {
	        minl=vt;
	        for (v=vs;v<=vt;++v)
	          if (minl>lev[v]&&map[u][v]>0)
	            minl=lev[v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        gap[lev[u]]++;
	        u=pre[u];
	      }
	  }
	return maxt;
}
int main()
{
	int i,j,toth=0,totl=0,t,ti,ans;
	char pic[50][50]; 
	freopen("meatchickwing.in","r",stdin);
	freopen("meatchickwing.out","w",stdout);
	scanf("%d",&t);
	for (ti=1;ti<=t;++ti)
	  {
	  	toth=totl=0;
	  	memset(map,0,sizeof(map));
	  	memset(pic,' ',sizeof(pic));
	    scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
	    for (i=1;i<=n;++i)
	      for (j=1;j<=m;++j)
	        cin>>pic[i][j];
	    for (i=1;i<=n;++i)
	      for (j=1;j<=m;++j)
	        {
	          if (pic[i][j]!=pic[i][j-1])
	            toth++;
	          dot[i][j].x=toth;
	        }
        for (j=1;j<=m;++j)
          for (i=1;i<=n;++i)
            {
              if (pic[i][j]!=pic[i-1][j])
                totl++;
              dot[i][j].y=toth+totl;
            }
        for (i=1;i<=toth;++i)
          map[0][i]=a;
        for (i=toth+1;i<=toth+totl;++i)
          map[i][toth+totl+1]=b;
        for (i=1;i<=n;++i)
          for (j=1;j<=m;++j)
            map[dot[i][j].x][dot[i][j].y]=c;
        ans=ISAP(0,toth+totl+1);
        printf("%d\n",ans);
        scanf("%*c");
      } 
    fclose(stdin);
    fclose(stdout);
}

CODEVS 1436 SCOI 修车

网络流最难的地方就是建模,对于这道题,我们先建立一个超级源点ss,超级汇点tt,然后从ss向每个车引一条流量为1,费用为0的边,在向第i车引向每个修车工一条流量为1,费用为0的边,再从顾客i和技师j的组合向技师j的修车顺序(这辆车是第k个修的)引一条流量为1,费用为k*time[i][j](因为要包含k个人等待的时间);再从技师j的修车顺序向tt引一条流量为1,费用为0的边,跑一遍最小费用最大流即可;理解是这样的,这样建点可以保证最小割一定是n辆车,由于最小割等于最大流,所以跑一遍最小费用最大流再平均一下就是答案;

PS:开循环队列开的稍大一点,程序会快一些,蒟蒻一开始就是因为这事T了好几遍。 code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c,w;
}a[100000];
int point[2000],next[100000],pre[2000],dis[2000],queue[1000000],minn[2000],head,tail,m,n,ans=0,e;
bool exist[2000];
void add(int i,int j,int c,int w)
{
	e++; next[e]=point[i]; point[i]=e;
	a[e].u=i; a[e].v=j; a[e].c=c; a[e].w=w;
	e++; next[e]=point[j]; point[j]=e;
	a[e].u=j; a[e].v=i; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	int u,i,stan;
	memset(exist,false,sizeof(exist));
	memset(dis,127,sizeof(dis));
	memset(minn,0,sizeof(minn));
	memset(pre,0,sizeof(pre));
	stan=dis[1]; minn[vs]=dis[1]; head=0; tail=1;
	dis[vs]=0; exist[vs]=true; queue[tail]=vs;
	while (head!=tail)
	  {
	    head=head%100000+1;
	    u=queue[head];
	    exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      {
	        if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
			  {
			    dis[a[i].v]=dis[u]+a[i].w;
			    pre[a[i].v]=i;
			    minn[a[i].v]=min(minn[u],a[i].c);
			    if (!exist[a[i].v])
			      {
			        tail=tail%1000000+1;
			        queue[tail]=a[i].v;
			        exist[a[i].v]=true;
			      }
			  } 
	      }
	  }
	if (dis[vt]==stan) return false;
	else
	  {
	    ans+=dis[vt]*minn[vt];
	    for (i=pre[vt];i!=0;i=pre[a[i].u])
	      {
	        a[i].c-=minn[vt];
	        a[i^1].c+=minn[vt];
	      }
	    return true;
	  }
}
int main()
{
	int i,j,k,dian=0;
	float avg;
	int pic[100][100],c[100][100],kk[100][100];
	scanf("%d%d",&m,&n);
	for (i=1;i<=n;++i)
	  for (j=1;j<=m;++j)
	    scanf("%d",&pic[i][j]);
	e=1;
	for (i=1;i<=n;++i)
	  add(0,i,1,0);
	dian=n;
	for (i=1;i<=n;++i)
	  for (j=1;j<=m;++j)
	    {
	      dian++; c[i][j]=dian;
	      add(i,dian,1,0);
	    }
	for (j=1;j<=m;++j)
	  for (k=1;k<=n;++k)
	    {
	      dian++; kk[j][k]=dian;
	    }
	for (i=1;i<=n;++i)
	  for (j=1;j<=m;++j)
	    for (k=1;k<=n;++k)
	      add(c[i][j],kk[j][k],1,k*pic[i][j]);
	++dian;
	for (j=1;j<=m;++j)
	  for (k=1;k<=n;++k)
	    add(kk[j][k],dian,1,0);
    while (work(0,dian));
    avg=ans/(n*1.0);
    printf("%0.2f\n",avg);
}

NOI 2012 美食节 

题意同上题,但是如果这样做的话,会T掉四个点,于是需要加优化,优化是只有当每个厨师处理掉上一道菜时,才加上下一道菜的边。

PS:循环队列开大了会快,但为毛数组开大了程序会慢呢,蒟蒻数组开大后,还不如裸的做法= = code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define len 1000000
using namespace std;
struct hp{
	int u,v,c,w;
}a[10000000];
int queue[1000000],pre[100000],dis[100000],next[10000000],minn[1000000],point[100000],n,m,e,head,tail,ans,dot;
bool exist[100000];
int ai[41],tim[41][101],c[101][101],lev[101],jian[100000],tt;
void add(int u,int v,int c,int w)
{
	e++; 
	next[e]=point[u]; point[u]=e;
	a[e].u=u; a[e].v=v; a[e].c=c; a[e].w=w;
	e++; 
	next[e]=point[v]; point[v]=e;
	a[e].u=v; a[e].v=u; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	int u,stan,i,cc;
	memset(exist,false,sizeof(exist));
	memset(dis,127,sizeof(dis));
	stan=minn[vs]=dis[1]; head=0; tail=1;
	dis[vs]=0; exist[vs]=true; queue[tail]=vs;
	while (head!=tail)
	  {
	    head=head%len+1;
	    u=queue[head]; 
		exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      {
	        if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
	          {
	            dis[a[i].v]=dis[u]+a[i].w;
	            pre[a[i].v]=i;
	            minn[a[i].v]=min(minn[u],a[i].c);
	            if (!exist[a[i].v])
	              {
	                tail=tail%len+1;
	                queue[tail]=a[i].v;
	                exist[a[i].v]=true;
	              }
	          }
	      }
	  }
	if (dis[vt]==stan) return false;
	    ans+=minn[vt]*dis[vt];
	    for (i=pre[vt];i!=0;i=pre[a[i].u])
	      {
	        a[i].c-=minn[vt];
	        a[i^1].c+=minn[vt];
	      }
	    cc=jian[a[pre[vt]].u];
	    ++lev[cc];  ++dot;
	    jian[dot]=cc;
	    for (i=1;i<=n;++i)
	      add(c[i][cc],dot,1,lev[cc]*tim[i][cc]);
	    add(dot,tt,1,0);
	    return true;
}
int main()
{
	int i,j,k;
	scanf("%d%d",&n,&m);
	e=1;
	for (i=1;i<=n;++i) 
	  { 
	    scanf("%d",&ai[i]);
	    add(0,i,ai[i],0);
      }
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j)
        scanf("%d",&tim[i][j]);
    dot=n;
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j)
        {
          dot++; c[i][j]=dot;
          add(i,c[i][j],ai[i],0);
        }
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j)
        {
          jian[dot+j]=j; lev[j]=1;
          add(c[i][j],dot+j,1,tim[i][j]);
        }
    dot+=m;
    dot++; tt=dot;
    for (j=1;j<=m;++j)
      add(dot-1-m+j,dot,1,0);
    while (work(0,tt));
    printf("%d\n",ans);
}

NOI 2006 最大获利:

noi里第一道网络流的题,其算法是最大权闭合子图,大概是源点向每个用户点连大小为C_i的边,每个中转站点向汇点连大小为P_i的边,每个用户向所对应的两个点连大小为正无穷的边,这样的话最小割一定不会把这三个点分开,然后最大流一边就行了。(PS:貌似需要各种神奇优化。带gap,cur优化的ISAP可过)

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 2100000000
using namespace std;
struct hp{
	int u,v,c;
}a[500000];
int e,n,m;
int p[5001];
int point[56000],next[500000],pre[56000],lev[56000],gap[56000];
int cur[56000];
int ISAP(int vs,int vt)
{
	int v,i,u,maxf=0,aug,minl; bool f;
	gap[0]=vt-vs+1; u=vs;
	while (lev[vs]<vt)
	  {
	    f=false;
		for (v=cur[u];v!=0;v=next[v])
		  if (lev[u]==lev[a[v].v]+1&&a[v].c>0)
		    {f=true; cur[u]=v; break;}
		if (f)
		  {
		    pre[a[v].v]=v;
		    u=a[v].v;
		    if (u==vt)
		      {
		        aug=inf;
		        for (i=v;i!=0;i=pre[a[i].u])
		          if (aug>a[i].c)
		            aug=a[i].c;
		        maxf+=aug;
		        for (i=v;i!=0;i=pre[a[i].u])
		          {
		            a[i].c-=aug;
		            a[i^1].c+=aug;
		          }
		        u=vs;
		      }
		  }
		else
		  {
		    minl=vt;
			for (i=point[u];i!=0;i=next[i])
			  if (minl>lev[a[i].v]&&a[i].c>0)
			    minl=lev[a[i].v];
		    gap[lev[u]]--;
			if (gap[lev[u]]==0) break;
			lev[u]=minl+1;
			cur[u]=point[u];
			gap[lev[u]]++;
			if (u!=vs) u=a[pre[u]].u;  
		  }	
	  }
	return maxf;
}
int main()
{
	int i,x,y,c,ans,sum=0;
	freopen("profit.in","r",stdin);
	freopen("profit.out","w",stdout);
	scanf("%d%d",&n,&m);
	e=1;
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&p[i]);
	    e++; 
	    next[e]=point[i]; cur[i]=point[i]=e; a[e].u=i; a[e].v=n+m+1; a[e].c=p[i];
	    e++;
	    next[e]=point[n+m+1]; cur[n+m+1]=next[n+m+1]=e; a[e].u=n+m+1; a[e].v=i; a[e].c=0;
      }
	for (i=1;i<=m;++i)
	  {
	    scanf("%d%d%d",&x,&y,&c); sum+=c;
		e++;
		next[e]=point[0]; cur[0]=point[0]=e; a[e].u=0; a[e].v=n+i; a[e].c=c;
		e++;
		next[e]=point[n+i]; cur[n+i]=point[n+i]=e; a[e].u=n+i; a[e].v=0; a[e].c=0;
		e++;
		next[e]=point[n+i]; cur[n+i]=point[n+i]=e; a[e].u=n+i; a[e].v=x; a[e].c=inf;
		e++;
		next[e]=point[x]; cur[x]=point[x]=e; a[e].u=x; a[e].v=n+i; a[e].c=0;
	    e++;
	    next[e]=point[n+i]; cur[n+i]=point[n+i]=e; a[e].u=n+i; a[e].v=y; a[e].c=inf;
	    e++;
	    next[e]=point[y]; cur[y]=point[y]=e; a[e].u=y; a[e].v=n+i; a[e].c=0;
      }
   ans=ISAP(0,n+m+1);
   printf("%d\n",sum-ans);
   fclose(stdin); fclose(stdout);
}

SDOI 2010 星际穿越

拆点的思路。对于每一个点,拆为两个点,第二个点表示已经经过(包括跑过去与跳过去 ),向汇点连边。第一个点表示未经过,由源点向其连边。code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct hp{
	int u,v,c,w;
}a[60000];
int point[2000],next[60000],n,m,e,ans=0,ai[801];
int queue[100000],dis[2000],pre[2000],minn[2000];
bool exist[2000];
void add(int x,int y,int c,int w)
{
	e++; next[e]=point[x]; point[x]=e; 
	a[e].u=x; a[e].v=y; a[e].c=c; a[e].w=w;
	e++; next[e]=point[y]; point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	memset(exist,false,sizeof(exist));
	memset(dis,127/3,sizeof(dis));
	memset(pre,0,sizeof(pre));
	int stan=dis[0],u,i,head,tail;
	head=0; tail=1; queue[tail]=vs; exist[vs]=true; minn[vs]=dis[0]; dis[vs]=0;
	while (head!=tail)
	  {
	    head=head%100000+1;
	    u=queue[head];
	    exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      if (dis[a[i].v]>dis[u]+a[i].w&&a[i].c>0)
	        {
	          dis[a[i].v]=dis[u]+a[i].w;
	          pre[a[i].v]=i;
	          minn[a[i].v]=min(minn[u],a[i].c);
	          if (!exist[a[i].v])
	            {
	              tail=tail%100000+1;
	              queue[tail]=a[i].v;
	              exist[a[i].v]=true;
	            }
	        }
	  }
	if (dis[vt]==stan) return false;
	ans+=dis[vt]*minn[vt];
	for (i=pre[vt];i!=0;i=pre[a[i].u])
	  {
	    a[i].c-=minn[vt];
	    a[i^1].c+=minn[vt];
	  }
	return true;
}
int main()
{
	int i,x,y,c;
	freopen("starrace.in","r",stdin);
	freopen("starrace.out","w",stdout);
	scanf("%d%d",&n,&m);
	e=1;
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&x);
		add(0,i*2-1,1,0);  
	    add(0,i*2,1,x);
	    add(i*2,n*2+1,1,0);
	  }
	for (i=1;i<=m;++i)
	  {
	    scanf("%d%d%d",&x,&y,&c);
	    if (x>y) swap(x,y);
	    x=x*2-1; y=y*2;
	    add(x,y,1,c);
	  }
	while (work(0,n*2+1));
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
}

BZOJ HNOI 2001 软件安装 同 网络流24题 餐巾规划

把餐巾按新旧分成两类,那么很容易考虑到把点拆为两个点,旧毛巾点可以分别留到下一天,也可以花费fa或fb的费用变为新毛巾点,新毛巾点也可以直接有超级源点花费f的费用直接买来,源点向旧毛巾点连边,新毛巾点向汇点连边,跑最小费用最大流即可。code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c,w;
}a[100001];
int num[10001],ans;
int point[30001],next[100001],n,ai,bi,fa,fb,f,e;
int queue[100001],dis[30001],pre[30001],minn[30001];
bool exist[30001];
void add(int x,int y,int c,int w)
{
	e++; next[e]=point[x]; point[x]=e;
	a[e].u=x; a[e].v=y; a[e].c=c; a[e].w=w;
	e++; next[e]=point[y]; point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	int head,tail,u,i,stan;
	memset(exist,false,sizeof(exist));
	memset(dis,127/3,sizeof(dis));
	memset(pre,0,sizeof(pre));
	head=0; tail=1; stan=minn[vs]=dis[0];
	dis[vs]=0; exist[vs]=true; queue[tail]=vs;
	while (head!=tail)
	  {
	    head=head%100000+1;
	    u=queue[head]; exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
            {
              dis[a[i].v]=dis[u]+a[i].w;
              pre[a[i].v]=i;
              minn[a[i].v]=min(minn[u],a[i].c);
              if (!exist[a[i].v])
                {
                  tail=tail%100000+1;
                  queue[tail]=a[i].v;
                  exist[a[i].v]=true;
                }
            }
	  }
	if (stan==dis[vt]) return false;
	ans+=minn[vt]*dis[vt];
	for (i=pre[vt];i!=0;i=pre[a[i].u])
	  {
	    a[i].c-=minn[vt];
	    a[i^1].c+=minn[vt];
	  }
	return true;
}
int main()
{
	int i,j;
	scanf("%d%d%d%d%d%d",&n,&ai,&bi,&f,&fa,&fb);
	e=1;
	for (i=1;i<=n;++i)
	  scanf("%d",&num[i]);
    for (i=1;i<=n;++i)
	  {
	    add(0,i*2-1,num[i],f);
	    add(0,i*2,num[i],0);
	    add(i*2-1,n*2+1,num[i],0);
      }
    for (i=1;i<=n-1;++i)
      add(i*2,(i+1)*2,2100000000,0);
	for (i=1;i<=n;++i)
	  {
	    if (i+ai+1<=n)
	      add(i*2,(i+ai+1)*2-1,2100000000,fa);
	    if (i+bi+1<=n)
	      add(i*2,(i+bi+1)*2-1,2100000000,fb);
	  }
	while (work(0,n*2+1));
	printf("%d\n",ans);
}

网络流24题 骑士共存 二分图最大匹配 code:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int a[700001]; 
bool visit[40001]={false};
int point[40001]={0},next[400001],match[40001]={0},e,n,m,dot[201][201]={0};
int dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1},t=0;
bool f[250][250]={0};
void add(int x,int y)
{
	a[++e]=y;
	next[e]=point[x]; point[x]=e; 
}
bool find(int u)
{
	int i;
	++t;
	for (i=point[u];i!=0;i=next[i])
	  {
	    if (!visit[a[i]])
		  {
		    visit[a[i]]=true;
		    if (!match[a[i]]||find(match[a[i]]))
		      {
		        match[a[i]]=u;
		        return true;
		      }
		  } 
	  }
	return false;
} 
int main()
{
	int i,x,y,j,k,ans=0,xx,yy,dotm=0,temp=0;
	freopen("knight.in","r",stdin);
	freopen("knight.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;++i)
	  {
	    scanf("%d%d",&x,&y);
	    f[x][y]=true;
	  }
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    {dot[i][j]=++dotm;}
	e=0;
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    if ((i+j)%2==0&&!f[i][j])
		  for (k=0;k<8;++k)
		    {
		      xx=i+dx[k];
		      yy=j+dy[k];
		      if (f[xx][yy]) continue;
		      if (xx<1||xx>n) continue;
		      if (yy<1||yy>n) continue;
		      add(dot[i][j],dot[xx][yy]);
		    }
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    if ((i+j)%2==0&&!f[i][j])
	      {
	      	temp++;
	        memset(visit,false,sizeof(visit));
	        if (find(dot[i][j]))
	          ans++;
	      }
	ans=n*n-m-ans;
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
}
网络流24题 数字梯形 最大不相交路径,注意答案2中路径也可以在终点相交 code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c;
}a[10000];
int next[10000],point[500];
int f[101][101];
bool d[201];
int cur[500],pre[500],lev[500],gap[500];
int m,n,e;
void add(int x,int y,int c)
{
	e++; next[e]=point[x]; cur[x]=point[x]=e;
	a[e].u=x; a[e].v=y; a[e].c=c;
	e++; next[e]=point[y]; cur[y]=point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0;
}
int ISAP(int vs,int vt)
{
	int u,v,i,maxf=0,aug,minl; bool f;
	memset(pre,0,sizeof(pre));
	memset(gap,0,sizeof(gap));
	memset(lev,0,sizeof(lev));
	gap[0]=vt-vs+1; u=vs;
	while (lev[vs]<vt)
	  {
	    f=false;
	    for (v=cur[u];v!=0;v=next[v])
	      if (a[v].c>0&&lev[u]==lev[a[v].v]+1)
	        {cur[u]=v; f=true; break;}
	    if (f)
	      {
	        pre[a[v].v]=v;
	        u=a[v].v;
	        if (u==vt)
	          {
	            aug=2100000000;
	            for (i=v;i!=0;i=pre[a[i].u])
	              if (aug>a[i].c)
	                aug=a[i].c;
	            maxf+=aug;
	            for (i=v;i!=0;i=pre[a[i].u])
	              {
	                a[i].c-=aug;
	                a[i^1].c+=aug;
	              }
	            u=vs;
	          }
	      }
	    else
	      {
	        minl=vt;
	        for (i=point[u];i!=0;i=next[i])
	          if (a[i].c>0&&minl>lev[a[i].v])
	            minl=lev[a[i].v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        cur[u]=point[u];
	        gap[lev[u]]++;
	        if (u!=vs) u=a[pre[u]].u;
	      }
	  }
	return maxf;
}
void find(int x)
{
	int i;
	d[x]=true;
	for (i=point[x];i!=0;i=next[i])
	  if (a[i].c&&!d[a[i].v]) find(a[i].v);
}
int main()
{
	int i,j,num,ans,x,sum=0; char c;
	freopen("shuttle.in","r",stdin);
	freopen("shuttle.out","w",stdout);
	scanf("%d%d",&m,&n);
	e=1;
	for (i=1;i<=m;++i)
	  {
	    scanf("%d",&x);
	    add(0,i,x);
	    sum+=x;
	    scanf("%*c%c",&c);
	    num=0;
	    while (c!='\n'&&c!='\r')
	      {
	        if (c==' ')
	          {
				f[i][0]++; f[i][f[i][0]]=m+num;
	          	add(i,m+num,2100000000);
	            num=0;
	          }
	        else
	          num=num*10+(int)(c)-48;
	        scanf("%c",&c);
	      }
	    f[i][0]++; f[i][f[i][0]]=m+num;
	    add(i,m+num,2100000000);
	  }
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&x);
	    add(m+i,m+n+1,x);
	  }
	ans=ISAP(0,n+m+1);
	find(0);
	for (i=1;i<=m;++i)
	  if (d[i]) printf("%d ",i);
	printf("\n");
	for (i=m+1;i<=n+m;++i)
	  if (d[i]) printf("%d ",i-m);
	printf("\n");
	ans=sum-ans;
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
}
太空飞行计划 最大权闭合子图,如果流量不为0则此方案在答案中出现,注意后悔边的存在 code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c;
}a[10000];
int next[10000],point[500];
int f[101][101];
bool d[201];
int cur[500],pre[500],lev[500],gap[500];
int m,n,e;
void add(int x,int y,int c)
{
	e++; next[e]=point[x]; cur[x]=point[x]=e;
	a[e].u=x; a[e].v=y; a[e].c=c;
	e++; next[e]=point[y]; cur[y]=point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0;
}
int ISAP(int vs,int vt)
{
	int u,v,i,maxf=0,aug,minl; bool f;
	memset(pre,0,sizeof(pre));
	memset(gap,0,sizeof(gap));
	memset(lev,0,sizeof(lev));
	gap[0]=vt-vs+1; u=vs;
	while (lev[vs]<vt)
	  {
	    f=false;
	    for (v=cur[u];v!=0;v=next[v])
	      if (a[v].c>0&&lev[u]==lev[a[v].v]+1)
	        {cur[u]=v; f=true; break;}
	    if (f)
	      {
	        pre[a[v].v]=v;
	        u=a[v].v;
	        if (u==vt)
	          {
	            aug=2100000000;
	            for (i=v;i!=0;i=pre[a[i].u])
	              if (aug>a[i].c)
	                aug=a[i].c;
	            maxf+=aug;
	            for (i=v;i!=0;i=pre[a[i].u])
	              {
	                a[i].c-=aug;
	                a[i^1].c+=aug;
	              }
	            u=vs;
	          }
	      }
	    else
	      {
	        minl=vt;
	        for (i=point[u];i!=0;i=next[i])
	          if (a[i].c>0&&minl>lev[a[i].v])
	            minl=lev[a[i].v];
	        gap[lev[u]]--;
	        if (gap[lev[u]]==0) break;
	        lev[u]=minl+1;
	        cur[u]=point[u];
	        gap[lev[u]]++;
	        if (u!=vs) u=a[pre[u]].u;
	      }
	  }
	return maxf;
}
void find(int x)
{
	int i;
	d[x]=true;
	for (i=point[x];i!=0;i=next[i])
	  if (a[i].c&&!d[a[i].v]) find(a[i].v);
}
int main()
{
	int i,j,num,ans,x,sum=0; char c;
	freopen("shuttle.in","r",stdin);
	freopen("shuttle.out","w",stdout);
	scanf("%d%d",&m,&n);
	e=1;
	for (i=1;i<=m;++i)
	  {
	    scanf("%d",&x);
	    add(0,i,x);
	    sum+=x;
	    scanf("%*c%c",&c);
	    num=0;
	    while (c!='\n'&&c!='\r')
	      {
	        if (c==' ')
	          {
				f[i][0]++; f[i][f[i][0]]=m+num;
	          	add(i,m+num,2100000000);
	            num=0;
	          }
	        else
	          num=num*10+(int)(c)-48;
	        scanf("%c",&c);
	      }
	    f[i][0]++; f[i][f[i][0]]=m+num;
	    add(i,m+num,2100000000);
	  }
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&x);
	    add(m+i,m+n+1,x);
	  }
	ans=ISAP(0,n+m+1);
	find(0);
	for (i=1;i<=m;++i)
	  if (d[i]) printf("%d ",i);
	printf("\n");
	for (i=m+1;i<=n+m;++i)
	  if (d[i]) printf("%d ",i-m);
	printf("\n");
	ans=sum-ans;
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
}
网络流24题 圆桌聚餐 建图很好想,但输出路径。。。 code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c;
}a[100000];
int point[500],next[100000],e,m,n;
int ansi[151][151],num[151];
int cur[500],pre[500],lev[500],gap[500];
void add(int x,int y,int c)
{
	e++; next[e]=point[x]; cur[x]=point[x]=e;
	a[e].u=x; a[e].v=y; a[e].c=c;
	e++; next[e]=point[y]; cur[y]=point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0;
}
int ISAP(int vs,int vt)
{
	int minl,maxf=0,aug,v,i,u; bool f,d;
    memset(pre,0,sizeof(pre));
    memset(gap,0,sizeof(gap));
    memset(lev,0,sizeof(lev));
    gap[0]=vt-vs+1; u=vs;
    while (lev[vs]<vt)
      {
        f=false;
        for (v=cur[u];v!=0;v=next[v])
          if (a[v].c>0&&lev[u]==lev[a[v].v]+1)
            {f=true; cur[u]=v; break;}
        if (f)
          {
            pre[a[v].v]=v;
            u=a[v].v;
            if (u==vt)
              {
                aug=2100000000;
                for (i=v;i!=0;i=pre[a[i].u])
                  if (aug>a[i].c)
                    aug=a[i].c;
                maxf+=aug;
                for (i=v;i!=0;i=pre[a[i].u])
                  {
                    a[i].c-=aug;
                    a[i^1].c+=aug;
                  }
                u=vs;
              }
          }
        else
          {
            minl=vt;
			for (i=point[u];i!=0;i=next[i])
			  if (a[i].c>0&&minl>lev[a[i].v])
			    minl=lev[a[i].v];
			gap[lev[u]]--;
			if (!gap[lev[u]]) break;
			lev[u]=minl+1;
			gap[lev[u]]++;
			cur[u]=point[u];
			if (u!=vs) u=a[pre[u]].u; 
          }
      }
    return maxf;
}
int main()
{
	int ans,i,j,sum=0,x;
	freopen("roundtable.in","r",stdin);
	freopen("roundtable.out","w",stdout);
	scanf("%d%d",&m,&n);
	e=1;
	for (i=1;i<=m;++i)
	  {
	    scanf("%d",&x); num[i]=x;
	    add(0,i,x);
	    sum+=x;
	  }
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&x);
	    add(m+i,n+m+1,x);
	  }
	for (i=1;i<=m;++i)
	  for (j=1;j<=n;++j)
	    add(i,j+m,1);
	ans=ISAP(0,n+m+1);
	if (ans==sum)
	  {
	    printf("1\n");
	    for (i=1;i<=m;++i)
	      {
	        for (j=point[i];j!=0;j=next[j])
	          if (a[j].c==0)
	            printf("%d ",a[j].v-m);
	        printf("\n");
	      }
	  }
	else
	  printf("0\n");
	fclose(stdin);
	fclose(stdout);
}
网络流24题 魔术球问题 贪心可解,还是蛮显然的,还发现一个公式 (n+1)^2/2+1,谁能告诉我为什么 code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int tp[61];
int main()
{
	int i=0,n,j;
	bool f;
	freopen("balla.in","r",stdin);
	freopen("balla.out","w",stdout);
	scanf("%d",&n);
	while (true)
	  {
	  	i++;
	  	f=false;
	    for (j=1;j<=n;++j)
	      if (sqrt(tp[j]+i)==floor(sqrt(tp[j]+i))||tp[j]==0)
	        {
	          tp[j]=i;
	          f=true;
	          break;
	        }
	    if (!f) break;
	  }
	printf("%d\n",i-1);
	fclose(stdin);
	fclose(stdout);
}
网络流24题 最长递增子序列 我只会费用流做法,但标解是最大流,速度稍慢,但还能接受(PS:我一开始居然想到了拆点 = =)code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct hp{
	int u,v,c,w;
}a[300000];
int n,num[501],e,maxf=0,maxn=0;
int point[501],next[300000];
int queue[100001];
int exist[501],dis[501],pre[501],minn[501];
void add(int x,int y,int c,int w)
{
	e++; next[e]=point[x]; point[x]=e;
	a[e].u=x ;a[e].v=y; a[e].c=c; a[e].w=w;
	e++; next[e]=point[y]; point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	int u,i,stan,head,tail;
	memset(exist,false,sizeof(exist));
	memset(dis,127/3,sizeof(dis));
	memset(pre,0,sizeof(pre));
	stan=minn[vs]=dis[0]; head=0; tail=1;
	dis[vs]=0; exist[vs]=true; queue[tail]=vs;
	while (head!=tail)
	  {
	    head=head%100000+1;
	    u=queue[head]; exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
	        {
	          dis[a[i].v]=dis[u]+a[i].w;
	          pre[a[i].v]=i;
	          minn[a[i].v]=min(minn[u],a[i].c);
	          if (!exist[a[i].v])
	            {
	              tail=tail%100000+1;
	              queue[tail]=a[i].v;
	              exist[a[i].v]=true;
	            }
	        }
	  }
	if (stan==dis[vt]||-dis[vt]<maxn) return false;
	maxf+=minn[vt];
	for (i=pre[vt];i!=0;i=pre[a[i].u])
	  {
	    a[i].c-=minn[vt];
	    a[i^1].c+=minn[vt];
	  }
	return true;
}
int main()
{
	int i,j;
	int f[501];
	freopen("alis.in","r",stdin);
	freopen("alis.out","w",stdout);
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	  scanf("%d",&num[i]);
	f[1]=1;
	for (i=2;i<=n;++i)
	  {
	  	f[i]=1;
	    for (j=1;j<=i-1;++j)
	      if (num[j]<=num[i]&&f[j]+1>f[i])
	        f[i]=f[j]+1;
	    maxn=max(maxn,f[i]);
      }
    printf("%d\n",maxn);
    e=1;
    for (i=1;i<=n;++i)
      {
      	add(0,i,1,0);
        add(i,n+1,1,-1);
        for (j=i+1;j<=n;++j)
          if (num[j]>=num[i])
            add(i,j,1,-1);
      }
    maxf=0;
    while (work(0,n+1)); printf("%d\n",maxf);
    memset(point,0,sizeof(point));
    memset(next,0,sizeof(next));
    e=1;
    for (i=1;i<=n;++i)
      {
      	if (i==1)
      	  add(0,i,2100000000,0);
      	else add(0,i,1,0);
        if (i==n)
          add(i,n+1,2100000000,-1);
        else add(i,n+1,1,-1);
        for (j=i+1;j<=n;++j)
          if (num[j]>=num[i])
            add(i,j,1,-1);
      }
    maxf=0;
    while (work(0,n+1)); printf("%d\n",maxf);
    fclose(stdin);
    fclose(stdout);
}
网络流24题 负载平衡 建图很巧妙,貌似用到了流量平衡的原理 code:
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 2100000000
using namespace std;
struct hp{
	int u,v,c,w;
}a[100001];
int n,ans,e,point[151],next[100001],num[101];
bool exist[151];
int queue[10001],pre[151],minn[151],dis[151];
void add(int x,int y,int c,int w)
{
	e++; next[e]=point[x]; point[x]=e;
	a[e].u=x; a[e].v=y; a[e].c=c; a[e].w=w;
	e++; next[e]=point[y]; point[y]=e;
	a[e].u=y; a[e].v=x; a[e].c=0; a[e].w=-w;
}
bool work(int vs,int vt)
{
	int u,stan,i,head,tail;
	memset(exist,false,sizeof(exist));
	memset(pre,0,sizeof(pre));
	memset(dis,127/3,sizeof(dis));
	minn[vs]=stan=dis[0]; head=0; tail=1;
	queue[tail]=vs; dis[vs]=0; exist[vs]=true;
	while (head!=tail)
	  {
	    head=head%10000+1;
	    u=queue[head]; exist[u]=false;
	    for (i=point[u];i!=0;i=next[i])
	      if (a[i].c>0&&dis[a[i].v]>dis[u]+a[i].w)
	        {
	          dis[a[i].v]=dis[u]+a[i].w;
	          pre[a[i].v]=i;
	          minn[a[i].v]=min(minn[u],a[i].c);
	          if (!exist[a[i].v])
	            {
	              exist[a[i].v]=true;
	              tail=tail%10000+1;
	              queue[tail]=a[i].v;
	            }
	        }
	  }
	if (stan==dis[vt]) return false;
	ans+=minn[vt]*dis[vt];
	for (i=pre[vt];i!=0;i=pre[a[i].u])
	  {
	    a[i].c-=minn[vt];
	    a[i^1].c+=minn[vt];
	  }
	return true;
}
int main()
{
	int i,sum=0,aver;
	freopen("overload.in","r",stdin);
	freopen("overload.out","w",stdout);
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	  {
	    scanf("%d",&num[i]);
	    sum+=num[i];
	  }
	aver=sum/n;
	e=1;
	for (i=1;i<=n;++i)
	  {
	    if (i+1<=n)
	      {add(i,i+1,inf,1); add(i+1,i,inf,1);}
	    else
	      {add(n,1,inf,1); add(1,n,inf,1);}
	    if (num[i]<aver)
	      add(i,n+1,aver-num[i],0);
	    if (num[i]>aver)
	      add(0,i,num[i]-aver,0);
	  }
	while (work(0,n+1));
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
}




你可能感兴趣的:(网络流学习记录)