题目大意
给一个无向图边权为一,添加若干条边使得到1节点的距离与想要的距离差的平方和最小。
解题思路
拆点,割掉边(i,j)-(i,j+1)表示dis[i]==j+1,容量为贡献,如果原来有边(i,j),那|dis[i]=dis[j]|<=1,连上(i,k)-(j,k-1),(j,k)-(i,k-1),容量为inf即可。跑最小割即出解。
code
using namespace std;
int const mn=1600+9,mm=500000+9,inf=1e9;
int t,n,gra=1,ans,map[50][50],a[mn],vis[mn],dis[mn],begin[mn],to[mm],size[mm],next[mm];
void insert2(int u,int v,int w){
to[++gra]=v;
size[gra]=w;
next[gra]=begin[u];
begin[u]=gra;
}
void insert(int u,int v,int w){
insert2(u,v,w);
insert2(v,u,0);
}
int add(int now,int target,int nflow){
vis[now]=1;
if(now==target){
ans+=nflow;
return nflow;
}
fr(i,now)if(size[i]&&(!vis[to[i]])&&(dis[now]==dis[to[i]]+1)){
int tmp=add(to[i],target,min(nflow,size[i]));
if(tmp){
size[i]-=tmp;
size[i^1]+=tmp;
return tmp;
}
}
return 0;
}
int updis(){
int tmp=inf;
fo(i,0,n*n+1)if(vis[i])
fr(j,i)
if(size[j]&&(!vis[to[j]]))
tmp=min(tmp,dis[to[j]]+1-dis[i]);
if(tmp==inf)return 0;
fo(i,0,n*n+1)if(vis[i])dis[i]+=tmp;
return 1;
}
int main(){
//freopen("fox.in","r",stdin);
//freopen("fox.out","w",stdout);
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
while(scanf("%d\n",&n)!=EOF){
fo(i,1,n){
fo(j,1,n){
char ch;
scanf("%c",&ch);
map[i][j]=ch=='Y';
}
scanf("\n");
}
fo(i,1,n)scanf("%d",&a[i]);
scanf("\n");
gra=1;fo(i,0,n*n+1)begin[i]=dis[i]=0;
insert(0,1,0);fo(i,0,n-2)insert(i*n+1,(i+1)*n+1,inf);
fo(i,2,n){
insert(0,i,inf);
fo(j,0,n-2)insert(j*n+i,(j+1)*n+i,pow(a[i]-j-1,2));
}
fo(i,1,n)insert(n*(n-1)+i,n*n+1,inf);
fo(i,1,n)fo(j,1,n)if(map[i][j])fo(k,0,n-2)
insert((k+1)*n+i,k*n+j,inf),insert((k+1)*n+j,k*n+i,inf);
ans=0;
do{
fo(i,0,n*n+1)vis[i]=0;
while(add(0,n*n+1,inf))
fo(i,0,n*n+1)vis[i]=0;
}while(updis());
printf("%d\n",ans);
}
return 0;
}