【apio2012】网络流

虽然apio没去,但是讲课资料还是值得研究

第2~4道题还没怎么见过,其他几道就比较老了

Transform Matrix

mt在wc2010的校内讲课中讲过,但是只记得听过这道题了,不过重新推发现思路还比较清晰,首先我们可以将任意1移至一个0而不影响其他的数字,假设是0就直接交换,如果是1,可以看做是接力,由于1是等价的,所以可以看做穿过去,那么我们就只需将初始状态的1与目标状态的1一一对应,并处理好了路线的限制就可以了,这部分可以直接最大流。

Road 通向城堡之路

其实这道题省选之前我都还没做,只听说做法比较奇葩,没想到还有网络流做法,一开始画化出两个不等式,想用流量平衡,发现连余项都添不上去,后来发现可正可负,意味着都不一定在等式两边出现两次,流量平衡就完全走不通了,看了题解发现,我忽略了一个题目性质,即如果用a[i]表示h[i]-h[i+1],假如a[i]-1,那么一定是某个a[j]+1只不过费用不同(可以构造,通过中间h的同升同降),我们的任务转成将所有a调整到指定区间[-c,c],调整方式是重新分配加1减1,并且有费用,这个就可以用带上下界的最小费用可行流实现。

这个题与下题的思路相反(等下会提到),主要是调整思想(这也是网络流经典优化方面),类似混合图的欧拉回路,通过流量来重新分配

这道题我用了原始对偶费用流,跑的比涛哥的奇葩算法快

#include <cstdio>
#include <cstdlib>
#include <cstring>
const long long oo=1073741819102LL;
int s,t,s1,t1,n,m,ss,time;
long long ans,phi;
int tail[100000],v[100000],flag[100000];
int next[2000000],sora[2000000],pow[2000000],st[2000000],p[100000],cost[2000000];
long long flow[2000000],ru[100000],chu[100000],d[100000],tmp,h[100000];
int spfa(int s,int t)
{
  int h,r,i,j,ne,na;
//  memset(d,127,sizeof(d));
  for (i=1;i<=t;i++) d[i]=oo;
  h=r=0;
  st[r=1]=t,d[t]=0,v[t]=1;
  for (;h<r;) {
    ne=st[++h];
    for (i=ne;next[i]!=0;) {
      i=next[i],na=sora[i];
      if (flow[pow[i]] && d[ne]+cost[pow[i]]<d[na]) {
	  d[na]=d[ne]+cost[pow[i]];p[na]=ne;
	if (!v[na]) v[na]=1,st[++r]=na;
      }
    }
    v[ne]=0;
  }
  if (d[s]>=oo) return 0;
  phi+=d[s];
  for (i=1;i<=t;i++) 
    for (j=i;next[j]!=0;) {
      j=next[j],ne=sora[j];
      cost[j]-=d[i]-d[ne];
    }
  return 1;
}
long long dfs(int x,long long low)
{
  if (t1==x) {ans+=phi*low;return low;}
  int i,ne;
  long long sum=0;
  flag[x]=time;
  for (i=x;next[i]!=0;) {
    i=next[i],ne=sora[i];
    if (flow[i] && !cost[i] && flag[ne]!=time) {
      if (flow[i]<low) tmp=dfs(ne,flow[i]);
      else tmp=dfs(ne,low);
      flow[i]-=tmp,flow[pow[i]]+=tmp,sum+=tmp,low-=tmp;
      if (!low) break;
    }
  }
  return sum;
}
void origin()
{
  s=n+1,t=s+1,s1=t+1,t1=s1+1,ss=t1;
  for (int i=1;i<=t1;i++) tail[i]=i,ru[i]=chu[i]=next[i]=0;
}
void link2(int x,int y,long long z,long long c)
{
  ss++,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,flow[ss]=z,cost[ss]=c,next[ss]=0;
  ss++,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,flow[ss]=0,cost[ss]=-c,next[ss]=0;
  pow[ss]=ss-1,pow[ss-1]=ss;
}
void link(int x,int y,long long low,long long lim,long long c)
{
  chu[x]+=low,ru[y]+=low;ans+=low*c;
  link2(x,y,lim-low,c);
}
void init()
{
  int i;
  long long x,c;
  scanf("%d%I64d\n",&n,&c);ans=0;
  origin();
  for (i=1;i<=n;i++) scanf("%I64d",&h[i]);
  for (i=1;i<=n-1;i++) {
    x=h[i]-h[i+1];
    //    if (x<-c) link(s,i,-c-x,c-x,1);else if (x>c) link(i,t,x-c,x+c,0);
    if (x<-c) link(i,t,-c-x,c-x,0);else if (x>c) link(s,i,x-c,x+c,0);
    else link(s,i,0,x+c,0),link(i,t,0,c-x,0);
  }
  for (i=1;i<=n-2;i++) link(i,i+1,0,oo,1),link(i+1,i,0,oo,1);
  link(t,s,0,oo,0);
  for (i=1;i<=t;i++)
    if (ru[i]>chu[i]) link2(s1,i,ru[i]-chu[i],0);
    else if (ru[i]<chu[i]) link2(i,t1,chu[i]-ru[i],0);
  for (phi=0;spfa(s1,t1);) 
    for (;time++, dfs(s1,oo) ;) ;
  for (i=s1;next[i]!=0;) {
    i=next[i];
    if (flow[i]) {printf("impossible\n");return ;}
  }
  printf("%I64d\n",ans);
}
int main()
{
  freopen("road.in","r",stdin);
  freopen("road.out","w",stdout);
   int t;
   scanf("%d\n",&t);
   for (;t;t--) init();
  return 0;
}


有道难题2010 建设乌托乡

求有道难题数据&题目

一张图,有特殊点和一般点, 每条边的端点中至少一个特殊点, 每个点有权值 L ,特殊点可以修改,代价为 c*|ΔL[ i ]|, ( i ,j ) 要计算代价,为 e*|L[ i ]-L[j]|,
min{c*Σ|ΔL[ i ]|+e*Σ|L[ i ]-L[j ]|}, L 只能为 1~20 的整数


我想这道题的时候,沿用了上道题的调整思想,但是中间状态过于复杂,所以想不下去了(真是弱),考虑没有特殊点相连的情况,那么任意一个特殊点取某一值的费用其实是确定的,但是如果有两个特殊点相连,那么改变一个点的L时互相之间就会产生新费用,忽略中间关系,我们试着用b[i,j]表示第i号点L设为j,建立一个图b[i,j]向b[i,j+1]的权为cost[i,j](不考虑特殊点的费用),假设特殊点<u,v>,b[u,j]向b[v,j]连权为e的边,那么最小割割开的时候,必有u与v的L值差的层中间的点的权e边被割开,就对应了一种费用方案,因此最小割即为所求。

主要思想应该是不考虑中间状态的变化,直接确定最终状态的费用(有点像动能定理)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值