考托福又不会考
Description
给出一个
N
*
方格的边上可以修墙,修每一面墙都有不同的费用。
接下来要修墙,修不同的墙需要不同的费用,修出来的所有墙必须是一堵可以自交的环形的墙,如下图是一个可行的例子:
红色的边为要修墙的边,红色的边组成一个自交的环形,例子是最优解,为
22
。
Data Constraint
•
1
<=
•
1
<=所有边权<=
Solution
集训队原来都喜欢做那么变态的题,改了我一个晚上有多,有毒……
首先,抛出一个结论,这个可以自交的环包含了从左上角走到每个城市左上角的最短路径,解释一下为什么
D
为某个城市的左上角,蓝色路径为最短路径,红色为环的一部分,此时,环的红色部分并没有包含最短路径,但此时我们可以发现,可以把从
接下来进行下一步操作,将网格图上的(
答案所需要的自交的环为在这些被拆开的点之间的边组成的路径之一。
接下来我们对这些点连上合法的边,跑一遍最短路即为答案了。
如图,网格点
A
,
对于每一个网格点,
1
与
特别地,对于被最短路径阻断的相邻的两点不能 连边,因为跑出来的环必须包含最短路径(不能穿过最短路径),如上图所示,
对于像
A4
和
C3
所属的网格点在网格图上相邻的点,之间要连上一条边权为
Cost
(
A
,
还有一个注意的地方,一个城市必须被环包含,为了能够保证每个城市都被包含,在这个城市内的点都不能连边,下图为一个连边模式图,红色的虚线是不能连的边,蓝色的实线是可以连的边(画的好丑啊╥﹏╥):
还有一点注意的,左上角的
2
,
最后以左上角的
跑最短路当然要用 dijkstra 算法,不然无法保证时间不会超限。
Code
\\好长啊╥﹏╥
#pragma GCC optimize(3)
#pragma G++ optimize(3)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)
using namespace std;
typedef long long ll;
const ll N=415,M=N*N,K=4*M,V=K*8,maxn=1e12;
ll zd[V];
struct Comp{
bool operator ()(int a,int b)
{return zd[a]!=zd[b] ? zd[a]<zd[b] : a<b ;}
};
set<int,Comp> op;
int ac[N][N],up[N][N],ne[V],la[V],lb[V],co[V];
int bz[M][5],city[N][N],zy[V];
int n,m,j,k,l,i,o,p;
int read()
{
int o=0;char ch=' ';
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
return o;
}
void dij1(int kk)
{
zd[1]=0; op.insert(1);
fo(i,2,kk)zd[i]=maxn,op.insert(i);
fo(i,1,kk-1)if(!op.empty()){
int k=*op.begin(); op.erase(k);
int ii=(k-1)/m+1,oo=(k-1)%m+1;
if(ii-1){
int bh=k-m,p=up[ii-1][oo];
if(zd[k]+p<zd[bh])op.erase(bh),zd[bh]=zd[k]+p,op.insert(bh),zy[bh]=m;
}
if(ii<n){
int bh=k+m,p=up[ii][oo];
if(zd[k]+p<zd[bh])op.erase(bh),zd[bh]=zd[k]+p,op.insert(bh),zy[bh]=-m;
}
if(oo-1){
int bh=k-1,p=ac[ii][oo-1];
if(zd[k]+p<zd[bh])op.erase(bh),zd[bh]=zd[k]+p,op.insert(bh),zy[bh]=1;
}
if(oo<m){
int bh=k+1,p=ac[ii][oo];
if(zd[k]+p<zd[bh])op.erase(bh),zd[bh]=zd[k]+p,op.insert(bh),zy[bh]=-1;
}
}
}
void dij2(int kk)
{
op.clear();
fo(i,3,kk)zd[i]=maxn,op.insert(i);
zd[2]=0; op.insert(2);
fo(i,2,kk-2)if(!op.empty()){
int k=*op.begin(); op.erase(k);
for(int y=la[k];y;y=ne[y])
if(zd[k]+co[y]<zd[lb[y]]){
op.erase(lb[y]); zd[lb[y]]=co[y]+zd[k];
op.insert(lb[y]);
}
}
}
void llb(int a,int b,int c)
{ne[++o]=la[a]; lb[o]=b; la[a]=o; co[o]=c;}
int main()
{
cin>>n>>m;
fo(i,1,n)fo(l,1,m)city[i][l]=read();
fo(i,1,n)fo(l,1,m+1)up[i][l]=read();
fo(i,1,n+1)fo(l,1,m)ac[i][l]=read();
int kk=0;
n++; m++;
dij1(n*m);
fo(i,1,n-1)
fo(l,1,m-1)
if(city[i][l]){
int u=(i-1)*m+l;
bz[u][3]=bz[u][4]=1;
bz[u+1][1]=bz[u+1][4]=1;
bz[u+m][2]=bz[u+m][3]=1;
bz[u+m+1][1]=bz[u+m+1][2]=1;
while(zy[u]!=0){
int y=u+zy[u];
if(zy[u]==-1)bz[u][1]=bz[y][3]=1;
if(zy[u]==1)bz[u][3]=bz[y][1]=1;
if(zy[u]==-m)bz[u][2]=bz[y][4]=1;
if(zy[u]==m)bz[u][4]=bz[y][2]=1;
u=y;
}
}
fo(i,1,n)
fo(l,1,m)
if(i*l!=1)
{
int y=(i-1)*m+l;
int u1=(y-1)*4+1,u2=u1+1,u3=u2+1,u4=u3+1;
if(!bz[y][1])llb(u1,u3,0),llb(u3,u1,0);
if(!bz[y][2])llb(u1,u2,0),llb(u2,u1,0);
if(!bz[y][3])llb(u2,u4,0),llb(u4,u2,0);
if(!bz[y][4])llb(u3,u4,0),llb(u4,u3,0);
}
if(!bz[1][4])llb(3,4,0),llb(4,3,0);
if(!bz[1][3])llb(2,4,0),llb(4,2,0);
fo(i,1,n-1)
fo(l,1,m-1){
int y1=(i-1)*m+l-1,y2=y1+1;
int y3=i*m+l-1,y4=y3+1;
y1*=4; y2*=4; y3*=4; y4*=4;
llb(y1+2,y2+1,ac[i][l]); llb(y2+1,y1+2,ac[i][l]);
llb(y2+4,y4+2,up[i][l+1]); llb(y4+2,y2+4,up[i][l+1]);
llb(y3+4,y4+3,ac[i+1][l]); llb(y4+3,y3+4,ac[i+1][l]);
llb(y1+3,y3+1,up[i][l]); llb(y3+1,y1+3,up[i][l]);
if(!city[i][l]){
llb(y1+4,y2+3,ac[i][l]); llb(y2+3,y1+4,ac[i][l]);
llb(y1+4,y3+2,up[i][l]); llb(y3+2,y1+4,up[i][l]);
llb(y3+2,y4+1,ac[i+1][l]); llb(y4+1,y3+2,ac[i+1][l]);
llb(y2+3,y4+1,up[i][l+1]); llb(y4+1,y2+3,up[i][l+1]);
}
}
dij2(n*m*4);
printf("%lld",zd[3]);
}