【BZOJ】4356: Ceoi2014 Wall-最短路

传送门:bzoj4356


题解

具体题解可以看这里

一开始读错题了,以为可以用多条环路来圈城市(不可做),实际上只能用一条从左上角出发并最终返回左上角的环路。

可以证明最优环路的一种可行解必然包含左上角点到每个城市左上角点的最短路径,所以先从左上角点出发跑出关于所有城市点的最短路DAG。

考虑把每个点拆成四个来控制方向:
在这里插入图片描述

如上图,每个点 ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 4 ) , ( 4 , 1 ) (1,2),(2,3),(3,4),(4,1) (1,2),(2,3),(3,4),(4,1)之间连代价为 0 0 0的无向边(转向),对于原图中的边,如 ( A , B ) (A,B) (A,B),连边 ( A 2 , B 1 ) , ( A 3 , B 4 ) (A2,B1),(A3,B4) (A2,B1),(A3,B4)

蓝色路线为最短路DAG上的边,注意必须经过这条边,所以不能切断它——即不能连接 ( A 1 , A 4 ) , ( A 2 , A 3 ) , ( B 1 , B 4 ) , ( B 4 , B 3 ) . . . (A1,A4),(A2,A3),(B1,B4),(B4,B3)... (A1,A4),(A2,A3),(B1,B4),(B4,B3)...
同样不能走到城市里面去,假设 A B C D ABCD ABCD围成一个城市,则 A 3 , B 4 , C 2 , D 1 A3,B4,C2,D1 A3,B4,C2,D1都不能走到。

强制不走左上角的 1 1 1,求左上角的 2 2 2 4 4 4的最短路就是答案。


代码

#include<bits/stdc++.h>
#define fi first
#define sc second 
#define gc getchar
#define pb push_back
#define rc(x,y) (((x-1))*(m+1)+(y))
#define RC(x,y,z) (((rc(x,y)-1)<<2)+z)
using namespace std;
const int N=404,MX=7e5+10,M=1e7+10;
typedef long long ll;
const ll inf=0x7f7f7f7f7f7f7f7f;
typedef double db;

int n,m,lim,pre[MX];
int hv[N][N],dn[N][N],rt[N][N];
int head[MX],to[M],nxt[M],w[M],tot;
ll dis[MX];bool ban[MX],vs[N*N],tg[MX];

char cp;
template<class T>inline void rd(T &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void lk(int u,int v,int vv)
{
	if(ban[u] || ban[v]) return;
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;
	to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=vv;
}

struct P{
	int id;ll v;
	bool operator <(const P&ky)const{
	    return v>ky.v;
	}
}tp;
priority_queue<P>que;
void dij(int st)
{
	int i,j,x;
	memset(dis,0x7f,sizeof(ll)*(lim+1));
	dis[st]=0;que.push((P){st,0LL});
	for(;!que.empty();){
		tp=que.top();que.pop();x=tp.id;
		if(dis[x]!=tp.v) continue;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(dis[j]<=dis[x]+w[i]) continue;
			pre[j]=x;dis[j]=dis[x]+w[i];
			que.push((P){j,dis[j]});
		}
	}
}

void mk(int x)
{
	if(vs[x] || !pre[x]) return;
	vs[x]=true;int y=pre[x],X=(x-1)<<2,Y=(y-1)<<2;
	if(y==x-1) tg[Y+2]=tg[X+4]=1;
	else if(y==x+1) tg[X+2]=tg[Y+4]=1;
	else if(y==x-m-1) tg[Y+3]=tg[X+1]=1;
	else tg[X+3]=tg[Y+1]=1;
	mk(y);
}

int main(){
	int i,j,k,x,y;
	rd(n);rd(m);
	for(i=1;i<=n;++i)
	 for(j=1;j<=m;++j)
	  rd(hv[i][j]);
	for(i=1;i<=n;++i)
	 for(j=1;j<=m+1;++j){
	 	rd(dn[i][j]);
	 	lk(rc(i,j),rc(i+1,j),dn[i][j]);
	 }
	for(i=1;i<=n+1;++i)
	 for(j=1;j<=m;++j){
	 	rd(rt[i][j]);
	 	lk(rc(i,j),rc(i,j+1),rt[i][j]);
	 }
	lim=(n+1)*(m+1);dij(1);
	memset(head,0,sizeof(int)*(lim+1));tot=0;
	for(i=1;i<=n;++i)
	 for(j=1;j<=m;++j)
	  if(hv[i][j]){
	  	mk(rc(i,j));
	  	ban[RC(i,j,3)]=ban[RC(i,j+1,4)]=ban[RC(i+1,j,2)]=ban[RC(i+1,j+1,1)]=true;
	  }
	ban[1]=true;
	for(i=1;i<=n+1;++i)
	 for(j=1;j<=m+1;++j)
	  for(x=(rc(i,j)-1)<<2,k=1;k<=4;++k)
	    if(!tg[x+k]) lk(x+k,x+k%4+1,0);
	for(i=1;i<=n;++i)
	 for(j=1;j<=m+1;++j)
	  lk(RC(i,j,3),RC(i+1,j,2),dn[i][j]),
	  lk(RC(i+1,j,1),RC(i,j,4),dn[i][j]);
	for(i=1;i<=n+1;++i)
	 for(j=1;j<=m;++j)
	  lk(RC(i,j,2),RC(i,j+1,1),rt[i][j]),
	  lk(RC(i,j+1,4),RC(i,j,3),rt[i][j]);
	lim<<=2;dij(2);
	printf("%lld",dis[4]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值