虽然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时互相之间就会产生新费用,忽略中间关系,我们试着用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边被割开,就对应了一种费用方案,因此最小割即为所求。
主要思想应该是不考虑中间状态的变化,直接确定最终状态的费用(有点像动能定理)