[最短路]luogu P6545 [CEOI2014]The Wall
题面
https://www.luogu.com.cn/problem/P6545
分析
一个定理:城墙必然包含左上角到各村庄的左上角的最短路
如何证明:考虑当前有一个城墙方案,不包含某条最短路,不难证明此时城墙必然将最短路分割为数段
将在被围住区域外的最短路补入城墙,原城墙部分删除,肯定更优,因为是最短路,而且扩增了城墙范围
多次这样补足之后,不难发现最优方案的城墙必然包含所有最短路
那么SPFA跑完最短路,然后此时城墙方案就变成了:不穿过最短路树的连边情况下跑出来的最短路
关于不穿过最短路,可以将一个方格的端点分为四个小端点点,在不穿过最短路的小端点之间连边权为 0 的边,方格上相邻的小端点连原边权,从左上角的一个小端点出发跑到左上角的另一个小端点即可
代码
#include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; typedef long long ll; const ll Inf=1ll<<62; const int N=410; struct Graph { int v,nx; ll w; }g[16*N*N]; int cnt,list[4*N*N],fa[4*N*N];//0times:rightup,1times:leftup,2times:leftdown,3times:rightdown ll w1[N][N],w2[N][N],dis[4*N*N]; int n,m,sum; struct Path { int u;ll dis; friend bool operator < (Path a,Path b) {return a.dis>b.dis;} }; bool vis[4*N*N],pth[N*N][4],vil[N][N];//0:down,1:up,2:left,3:right void Add(int u,int v,ll w) {g[++cnt]=(Graph){v,list[u],w};list[u]=cnt;g[++cnt]=(Graph){u,list[v],w};list[v]=cnt;} int id(int i,int j) {return (i-1)*(m+1)+j;} void Dijkstra(int v0) { priority_queue<Path> q; while (!q.empty()) q.pop(); memset(vis,0,sizeof vis); for (int i=1;i<=4*sum;i++) dis[i]=Inf; q.push((Path){v0,dis[v0]=0}); while (!q.empty()) { Path u=q.top();q.pop(); if (vis[u.u]) continue;vis[u.u]=1; for (int i=list[u.u];i;i=g[i].nx) if (dis[g[i].v]>dis[u.u]+g[i].w) q.push((Path){g[i].v,dis[g[i].v]=dis[fa[g[i].v]=u.u]+g[i].w}); } } int main() { scanf("%d%d",&n,&m);sum=(n+1)*(m+1); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&vil[i][j]); for (int i=1;i<=n;i++) for (int j=1;j<=m+1;j++) scanf("%lld",&w1[i][j]),Add(id(i,j),id(i+1,j),w1[i][j]); for (int i=1;i<=n+1;i++) for (int j=1;j<=m;j++) scanf("%lld",&w2[i][j]),Add(id(i,j),id(i,j+1),w2[i][j]); Dijkstra(1); cnt=0;memset(list,0,sizeof list); for (int i=1;i<=n;i++) for (int j=1,u;j<=m;j++) if (vil[i][j]) { u=id(i,j); while (u!=1) { if (u-fa[u]==m+1) pth[u][0]=pth[fa[u]][1]=1; if (u-fa[u]==-(m+1)) pth[fa[u]][0]=pth[u][1]=1; if (u-fa[u]==1) pth[u][2]=pth[fa[u]][3]=1; if (u-fa[u]==-1) pth[fa[u]][2]=pth[u][3]=1; u=fa[u]; } } for (int i=1;i<=n+1;i++) for (int j=1;j<=m+1;j++) { if (!(i==1&&j==1)&&!pth[id(i,j)][0]&&!vil[i-1][j-1]&&!vil[i-1][j]) Add(id(i,j),id(i,j)+sum,0); if (!(i==1&&j==1)&&!pth[id(i,j)][2]&&!vil[i-1][j-1]&&!vil[i][j-1]) Add(id(i,j),id(i,j)+3*sum,0); if (!pth[id(i,j)][1]&&!vil[i][j]&&!vil[i][j-1]) Add(id(i,j)+2*sum,id(i,j)+3*sum,0); if (!pth[id(i,j)][3]&&!vil[i][j]&&!vil[i-1][j]) Add(id(i,j)+sum,id(i,j)+2*sum,0); if (i<=n) Add(id(i,j)+3*sum,id(i+1,j),w1[i][j]),Add(id(i,j)+2*sum,id(i+1,j)+sum,w1[i][j]); if (j<=m) Add(id(i,j)+sum,id(i,j+1),w2[i][j]),Add(id(i,j)+2*sum,id(i,j+1)+3*sum,w2[i][j]); } Dijkstra(1+sum); printf("%lld\n",dis[1+3*sum]); }
在日渐沉没的世界里,我发现了你。