zoukankan      html  css  js  c++  java
  • 题解 Luogu P2499: [SDOI2012]象棋

    关于这道题, 我们可以发现移动顺序不会改变答案, 具体来说, 我们有以下引理成立:

    • 对于一个移动过程中的任意一个移动, 若其到达的位置上有一个棋子, 则该方案要么不能将所有棋子移动到最终位置, 要么可以通过改变顺序使这一次移动合法

    证明:

    考虑到达位置上的那个棋子, 如果它没有到达最终位置, 则我们考虑将该棋子移至下一步, 如果下一步还有没有到达最终位置的棋子, 则也移动它

    否则直接调换这两个棋子的移动顺序即可

    好的我们去除了题目中的要求: 「移动过程中不能出现多颗棋子同时在某一格的情况」, 接下来考虑题目被我们转化成了什么样子:

    给定(k)个起始点和它们到达(k)个终点的步数, 求一种移动方案, 使得这种移动方案能将所有起始点移动到终点(保证有方案), 其次使得所花费的步数最少.

    于是我们把这道题目转化为了一个费用流问题, 具体的, 我们建立超级源点(S), 超级汇点(T), 接下来连接三类边

    1. (S ightarrow)所有起始点, 这类边容量为1, 费用为0

    2. 每一个起始点( ightarrow)每一个它能够到达的终点, 这类边容量为1, 费用为所消耗的步数

    3. 所有终点( ightarrow T), 这类边容量为1, 费用为0

    在新图上跑费用流即可AC

    要是这道题有这么简单就好了

    如果你使用了一般的EK+SPFA, 你会像我这样:

    接下来我们考虑改进算法, ouuan在这篇帖子中说道:

    常见费用流算法(把 Dinic 的 BFS 改成求最短路)复杂度上界是 (O(nmf)),其中 (f) 是最大流。

    于是我们点进了下面的链接学习了那种方法并且AC本题

    我不会那种方法>_< 于是使用了Dijkstra跑最短路, 事实上一般的Dijkstra是不能跑的, 但是我在费用流的题解中找到了这种神仙做法

    具体来说, 我们定义势函数(h(i))使得(e'(u,v)=e(u,v)+h(u)-h(v)), 最后对最短路的影响可以强行考虑掉, 且(e'(u,v))恒非负, 就可Dijkstra了

    接下来我们发现(h(i)=mindis(u))是成立的, 但那样的话你还要跑SPFA求最短路

    因此我们转而考虑(h(i)=mindis_{round-1}(u)), 其中(mindis_{round-1}(u))指上一轮(S ightarrow u)的最短路(不存在为0), 我们发现这也是成立的, 具体来说, 我们考虑每条边(e(u,v)).

    • (e(u,v))在上一次增广时存在, 那么显然满足性质
    • 否则(e(u,v))在最短路上, 也可证明(e'(u,v) geq 0)

    于是我们的势算法成立, 用Dijkstra替换SPFA即可在luogu上AC本题

    #pragma GCC diagnostic error "-std=c++11"
    #pragma GCC target("avx")
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-fwhole-program")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-fstrict-overflow")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-skip-blocks")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("-funsafe-loop-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    #include<bits/stdc++.h>
    using namespace std;
    namespace mcmf {
    	const int N=1002; const int M=500*502*2+1; const int inf=0x3f3f3f3f;
        int tot = 1, head[N], Next[M * 2], ver[M * 2], edge[M * 2], cost[M * 2];
        int dis[N], h[N], lst[N], pre[N], C, n;
    	#define pi pair<int,int>
        inline void _add(int u, int v, int c, int w) {
            Next[++tot] = head[u], head[u] = tot, ver[tot] = v, edge[tot] = c, cost[tot] = w;
        }
        inline void add(int u, int v, int c, int w) {
            _add(u, v, c, w), _add(v, u, 0, -w);
        }
        void Dijkstra(int s) {
        	priority_queue<pi, vector<pi>, greater<pi> > q;
            for(; !q.empty(); q.pop());
            fill(dis, dis + 1 + n, -1);
            dis[s] = 0, q.push(pi(0, s));
            while(!q.empty()) {
                pi now = q.top(); q.pop();
                int u = now.second;
                if(dis[u] < now.first) continue;
                for(int i = head[u]; i; i = Next[i]) {
                    static int v; v = ver[i];
                    if(!edge[i]) continue;
                    if(dis[v] < 0 || dis[v] > dis[u] + cost[i] + h[u] - h[v]) {
                        dis[v] = dis[u] + cost[i] + h[u] - h[v];
                        pre[v] = u, lst[v] = i;
                        q.push(pi(dis[v], v));
                    }
                }
            }
        }
        int solve(int s, int t) {
            memset(h,0,sizeof(h)); int d=inf;
            while(1){
                Dijkstra(s); if(dis[t] < 0) break;
                for(register int i = 1; i <= n; ++i) h[i] += (dis[i] != -1) ? dis[i] : 0; d=inf;
                for(int u = t; u != s; u = pre[u]) edge[lst[u]]<d && (d=edge[lst[u]]); C += h[t] * d;
                for(int u = t; u != s; u = pre[u]) edge[lst[u]] -= d, edge[lst[u] ^ 1] += d;
            } 
    		return C;
        }
    }
    const int _=501,__=101;
    int n,m,k,a,b;
    int un[__][__],dis[__][__];
    struct pos{
    	int x,y;
    }st[_], ed[_];
    queue<pos> Q;
    int dx[8]={1,1,-1,-1,1,1,-1,-1}, dy[8]={1,-1,1,-1,1,-1,1,-1};
    #define in(a) (a.x>=1 && a.x<=n && a.y>=0 && a.y<=m && !un[a.x][a.y])
    void getdis(int s){
    	Q.push(st[s]); dis[st[s].x][st[s].y]=0; pos x; int d;
    	while(!Q.empty()){
    		x=Q.front(); d=dis[x.x][x.y]; Q.pop();
    		for(int i=0;i<8;++i){
    			x.x+=dx[i]; x.y+=dy[i];
    			(dis[x.x][x.y]==-1) && in(x) && (Q.push(x),dis[x.x][x.y]=d+1);
    			x.x-=dx[i]; x.y-=dy[i];
    		}
    	}
    }
    char get(){char c=getchar(); while(c!='*' && c!='.') c=getchar(); return c;}
    int main(){
    	scanf("%d%d%d%d%d",&n,&m,&k,&a,&b); 
    	for(register int i=1;i<=n;++i) for(int j=1;j<=m;++j) {
    		char ch=get(); un[i][j]=(ch=='*');
    	}
    	for(register int i=0;i<4;++i) dx[i]*=a,dy[i]*=b;
    	for(register int i=4;i<8;++i) dx[i]*=b,dy[i]*=a;
    	for(register int i=1;i<=k;++i) scanf("%d%d",&st[i].x,&st[i].y);
    	for(register int i=1;i<=k;++i) scanf("%d%d",&ed[i].x,&ed[i].y);
    	for(register int i=1;i<=k;++i) {
    		memset(dis,-1,sizeof(dis)); getdis(i);
    		for(int j=1;j<=k;++j) if(dis[ed[j].x][ed[j].y]!=-1) mcmf::add(i+1,j+k+1,1,dis[ed[j].x][ed[j].y]);
    	}
    	for(int i=1;i<=k;++i) mcmf::add(1,i+1,1,0),mcmf::add(i+k+1,2*k+2,1,0);
    	mcmf::n=2*k+2; printf("%d
    ",mcmf::solve(1,2*k+2));
    }
    
  • 相关阅读:
    区块链到底是什么?
    Focusky:把每个PPT都变成3D动画
    c# 嵌入资源文件
    向ArcGIS的ToolBarControl中添加任意的windows控件的方法
    C# 获得MP4时长
    arcmap Command
    C# PPT 查找替换
    C# 操作PPt,去掉文本框的边框
    arcgis 按面积分割, 按比例分割面积,按等份批量面积分割工具
    电动自行车如何过马路?规定:下车推行!
  • 原文地址:https://www.cnblogs.com/tyqtyq/p/12024011.html
Copyright © 2011-2022 走看看