• [最短路]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
    ",dis[1+3*sum]);
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    不用π求坐标夹角大小
    使用LVS实现负载均衡原理及安装配置详解
    从dfs向动态规划过渡
    关于dfs
    [LeetCode] Add Two Numbers
    [LeetCode] Gray Code
    [LeetCode] Single Number
    第四章 深入JSP技术
    蚂蚁破2万亿!身价暴涨2077亿的彭蕾:无论马云的决定是什么,我都让它成为最正确的决定...
    异常场景测试
  • 原文地址:https://www.cnblogs.com/mastervan/p/14578718.html
Copyright © 2020-2023  润新知