这道题今天我们考试考到了,第三题,最后只剩半小时了,随便打了个暴搜,最后竟然还没调完QAQ,我竟然连暴力都不会打了

咳咳,不扯了,下面开始说这道题的做法

由于N和M都不大于150最容易想到的是Floyd(其实是Dijkstra不会写)于是就有了复杂度为O($n^6$)的25分算法

由此,我们可以得到以下的25分做法(考试时25分,洛谷嘛......0分)
代码如下(机房某个大佬写的):

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define rep(i,a,b) for (register int i(a);i<=(b);i++)
using namespace std;
template <typename T>
int read(T &x) {x=0;int f=1;char c=getchar();for(; !isdigit(c); c=getchar()) if(c=='-') f=-f;for(; isdigit(c); c=getchar()) x=x*10+c-'0';x*=f;
}
int ans,n,m,b[20][20],a[20][20],c[20][20][20][20],x1,x2,x3,z1,z2,z3;
char t;
int main(){freopen("zhber.in","r",stdin);freopen("zhber.out","w",stdout);read(n),read(m);if (n*m>450){printf("NO\n");return 0;}memset(b,0,sizeof b);memset(a,0,sizeof a);memset(c,0,sizeof c);rep(i,1,n)rep(j,1,m) read(b[i][j]);rep(i,1,n)rep(j,1,m) read(a[i][j]);read(x1),read(z1),read(x2),read(z2),read(x3),read(z3);rep(i1,1,n)rep(j1,1,m)rep(i2,1,n)rep(j2,1,m)if ((i1==i2)&&(j1==j2)) c[i1][j1][i1][j1]=0;else{int d=abs(i1-i2)+abs(j1-j2);if (b[i1][j1]>=d) c[i1][j1][i2][j2]=a[i1][j1];else c[i1][j1][i2][j2]=INF;if (b[i2][j2]>=d) c[i2][j2][i1][j1]=a[i2][j2];else c[i2][j2][i1][j1]=INF;}rep(i1,1,n)rep(j1,1,m)rep(i2,1,n)rep(j2,1,m)rep(i3,1,n)rep(j3,1,m)if (c[i2][j2][i3][j3]>(c[i2][j2][i1][j1]+c[i1][j1][i3][j3])) c[i2][j2][i3][j3]=c[i2][j2][i1][j1]+c[i1][j1][i3][j3];ans=INF;if (ans>c[x1][z1][x2][z2]+c[x3][z3][x2][z2]) ans=c[x1][z1][x2][z2]+c[x3][z3][x2][z2],t='X';if (ans>c[x2][z2][x1][z1]+c[x3][z3][x1][z1]) ans=c[x2][z2][x1][z1]+c[x3][z3][x1][z1],t='Y';if (ans>c[x1][z1][x3][z3]+c[x2][z2][x3][z3]) ans=c[x1][z1][x3][z3]+c[x2][z2][x3][z3],t='Z';if (ans<INF) printf("%c\n%d\n",t,ans);else printf("NO\n");return 0;
}

emmmmmm,其实我就是凑字数

看一下没什么不好2333,下面有好东西,Be patient~

好了,言归正传。

首先,我们注意到这是一个最短路问题,那么自然而然就想到了Dijkstra算法(不要管SPFA,她已经死了)。但是本题和纯的单元最短路径略有不同,需要转化一下。

那么,最容易想到的是在每个点和它可以到达的点之间加一条边,然而,这样一来,边总数的最大值是$150^4$$*2=1012500000

自然,这是存不下的(洛谷上空间给了512MB我们考试才给128MB),所以不建立边,直接把在某个点弹射范围内的点扫一遍,然后全部压到队列里去,这样就可以写出这样的代码(复杂度:O($(nm)^2$log(n$$m))):

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define REP(i,a,b) for (register int i(a);i<=(b);i++)
using namespace std;
int d,Da,Ans,n,m,Dist[160][160],x[5],y[5],p[5][5],a[160][160],b[160][160];
struct Node {int x,y,q;Node(int xx,int yy,int qq) {x=xx;y=yy;q=qq;}
};
bool operator < (Node a,Node b) {return a.q>b.q;
}
template <typename T>
int Read(T &x) {x=0;int f=1;char c=getchar();while(c!='-'&&c>'9'&&c<'0')c=getchar();for(; !isdigit(c); c=getchar())if(c=='-')f=-f;for(; isdigit(c); c=getchar())x=x*10+c-'0';x*=f;if(c=='\n')return 1;elsereturn 0;
}
void Write(long long x) {if(x<0) {putchar('-');x=-x;}if(x>9) {Write((x-x%10)/10);}putchar(x%10+'0');
}
inline void Dijkstra(int k) {REP(i,1,n) {REP(j,1,m) {Dist[i][j]=INF;}}priority_queue<Node> Q;Q.push(Node(x[k],y[k],0));Dist[x[k]][y[k]]=0;while(!Q.empty()) {Node X=Q.top();Q.pop();if(Dist[X.x][X.y]!=X.q) {continue;}int Len=b[X.x][X.y],v=Dist[X.x][X.y]+a[X.x][X.y];REP(i,max(1,X.x-Len),min(n,X.x+Len)) {int Tmp=Len-abs(X.x-i);REP(j,max(1,X.y-Tmp),min(m,X.y+Tmp)) {if(Dist[i][j]>v) {Dist[i][j]=v;Q.push(Node(i,j,Dist[i][j]));}}}}REP(i,1,3) {p[k][i]=Dist[x[i]][y[i]];}
}
int main() {Read(n);Read(m);REP(i,1,n) {REP(j,1,m) {Read(b[i][j]);}}REP(i,1,n) {REP(j,1,m) {Read(a[i][j]);}}REP(i,1,3) {Read(x[i]);Read(y[i]);}REP(i,1,3) {Dijkstra(i);}int Da=0,Ans=INF;REP(i,1,3) {d=0;d=p[1][i]+p[2][i]+p[3][i];if(d<Ans) {Da=i;Ans=d;}}if(Ans==INF) {puts("NO");} else {switch(Da) {case 0: {puts("NO");break;}case 1: {puts("X");Write(Ans);break;}case 2: {puts("Y");Write(Ans);break;}case 3: {puts("Z");Write(Ans);break;}}}return 0;
}

这个代码在洛谷上可以AC,但考试时只有60分(因为洛谷时限时5000ms,我们学校的是1000ms)QAQ

所以还要优化,于是有人写出了线段树优化Dijkstra(复杂度:我不会算啊QAQ,哪个大佬教教我)

代码我懒得写了看一下下面大佬的题解吧

当然,还有我们机房的一个神仙写出了二维线段树(复杂度我同样不会算QAQ,但是似乎比一般线段树快不少呢)
(代码来自Centaurus99巨佬%%%%)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read(){int x=0,f=1;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return x*f;
}
const int N=160,INF=0x3f3f3f3f;
int n,m,a[N][N],b[N][N],tag[N][N];
struct node{int v,x;
};
inline bool operator<(const node&t1,const node&t2){return t1.v<t2.v||(t1.v==t2.v&&t1.x>t2.x);}
inline void inc(node&x,node&v){x=(v<x?v:x);}
inline void inc(int&x,int&v){x=(v<x?v:x);}
struct DATA{node mn;int addv;
};
struct SGT{DATA T[N<<2];inline void create(int o,int l,int r){T[o].mn.v=INF,T[o].addv=INF;if (l==r){T[o].mn.x=l;return;}int mid=(l+r)>>1;create(o<<1,l,mid),create(o<<1|1,mid+1,r);pushup(o);}inline void pushup(int o){T[o].mn=min(T[o<<1].mn,T[o<<1|1].mn);}inline void pushdown(int o){if (T[o].addv!=INF){if (T[o<<1].mn.x) inc(T[o<<1].mn.v,T[o].addv),inc(T[o<<1].addv,T[o].addv);if (T[o<<1|1].mn.x) inc(T[o<<1|1].mn.v,T[o].addv),inc(T[o<<1|1].addv,T[o].addv);T[o].addv=INF;}}inline void qadd(int o,int l,int r,int x,int y,int v){if (T[o].mn.x==0) return;if (x<=l&&r<=y){inc(T[o].mn.v,v),inc(T[o].addv,v);return;}pushdown(o);int mid=(l+r)>>1;if (y<=mid) qadd(o<<1,l,mid,x,y,v);else if (x>mid) qadd(o<<1|1,mid+1,r,x,y,v);else qadd(o<<1,l,mid,x,y,v),qadd(o<<1|1,mid+1,r,x,y,v);pushup(o);}inline void qset(int o,int l,int r,int x,int v){if (l==r){T[o].mn.v=v;if (v==INF) T[o].mn.x=0;return;}pushdown(o);int mid=(l+r)>>1;if (x<=mid) qset(o<<1,l,mid,x,v);else qset(o<<1|1,mid+1,r,x,v);pushup(o);}
}T[N];
struct PLA{int x,y;
}S[4];
int G[4][4];
inline void update(int x,int y,int v){int lim=min(n,x+b[x][y]);for (int i=max(1,x-b[x][y]);i<=lim;++i){int l=max(1,y-b[x][y]+abs(i-x)),r=min(m,y+b[x][y]-abs(i-x));T[i].qadd(1,1,m,l,r,v+a[x][y]);}
}
void dij(int s){int t1,t2;if (s==1) t1=2,t2=3;else if (s==2) t1=1,t2=3;else t1=1,t2=2;for (int i=1;i<=n;++i) T[i].create(1,1,m);T[S[s].x].qset(1,1,m,S[s].y,0);while (G[s][t1]==-1||G[s][t2]==-1){node u;u.v=INF;int ux=0;for (int i=1;i<=n;++i){node t=T[i].T[1].mn;if (t<u) u=t,ux=i;}if (u.x==0||ux==0) break;if (ux==S[t1].x&&u.x==S[t1].y) G[s][t1]=u.v;if (ux==S[t2].x&&u.x==S[t2].y) G[s][t2]=u.v;T[ux].qset(1,1,m,u.x,INF);update(ux,u.x,u.v);}
}
int main(){n=read(),m=read();for (int i=1;i<=n;++i){for (int j=1;j<=m;++j) b[i][j]=read();}for (int i=1;i<=n;++i){for (int j=1;j<=m;++j) a[i][j]=read();}for (int i=1;i<=3;++i) S[i].x=read(),S[i].y=read(),tag[S[i].x][S[i].y]=i;memset(G,-1,sizeof(G));int tans=INF,tip=0;for (int i=1;i<=3;++i) dij(i);for (int i=1;i<=3;++i){int now=0;for (int j=1;j<=3;++j)if(i!=j){if (G[j][i]==-1){now=INF;break;}now+=G[j][i];}if (now<tans) tans=now,tip=i;}if (tip==0) return printf("NO\n"),0;else if (tip==1) printf("X\n");else if (tip==2) printf("Y\n");else printf("Z\n");printf("%d\n",tans);return 0;
}

当然,也有人写分层图Dijkstra的,题解里有人发了,我就不写了(其实是懒得写2333)

下面重点来了

敲黑板~

上面的算法都比较优秀的解决了这道题(尤其是后面2种),但是......

还不够快

下面介绍一种Dijkstra+并查集优化的算法,这是我在网上看到的,原帖并没有讲解,那我来说一下吧

本题中,每一个点都被扫到很多次,如果当这个点不再被需要更新时跳过这个点,就可以节省很多时间,这个算法就是基于这样一种思想。

这道题中,经过仔(yi)细(dun)观(luan)察(gao)我们可以发现,如果把点压入堆中时,比较Dist[i]+w[i]而不是比较Dist[i],就可以保证已经更新过的的点以后不再需要更新。
证明(我这么菜,当然不会证了QAQ,以下证明是机房里zcysky和Holyk两位巨佬在一个月黑风高的夜晚想出来的,在此引用一下,希望他们别打我):

假设点 x 已经得到了最短路,证明用该点更新的 y也得到了最短路

反证,假设存在路径 x′→y

使 dis[y] 更小,且在 x 更新 y 之后,那么有 dis[x′]+w[x]<dis[x]+w[x] ,因为 x′ 在 x 之后,有 dis[x′]+w[x′]≥dis[x]+w[x],两式矛盾,运用数学归纳法,可知上述结论成立,以及起点 s 到每一点的最短路径就是 dis[i]

于是我们可以每一行建立一个并查集来跳过已经松弛过的节点,这样代码效率就会大大提高(复杂度:$O(nm)log(n*m)$)
代码如下:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define REP(i,a,b) for (register int i(a);i<=(b);i++)
namespace fast_IO {const int IN_LEN=10000000,OUT_LEN=10000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf;char *lastin=ibuf+IN_LEN;const char *lastout=ibuf+OUT_LEN-1;inline char getchar_() {if(ih==lastin)lastin=ibuf+fread(ibuf,1,IN_LEN,stdin),ih=ibuf;return (*ih++);} inline void putchar_(const char x) {if(ih==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;} inline void flush() {fwrite(obuf, 1, oh - obuf, stdout);}
}
using namespace fast_IO;
template <typename T>
inline void Read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)) {if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;
}
template <typename T>
void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0');
}
template <typename T>
inline void Write(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x);
}
//玄学的读入优化
using namespace std;
int n,m,Res,Da,x[5],y[5],a[160][160],b[160][160],Top[160][160];//Top是并查集
long long Dist[160][160],Ans[5];//Dist数组存离原点的距离,Ans数组存当到前点会合的最小代价
struct Node {long long Dis;int x,y;Node() {} Node(int x,int y,long long Dis):Dis(Dis),x(x),y(y) {}
};
bool operator <(Node a,Node b) {return a.Dis>b.Dis;
}
int T(int Top[],int x) {return Top[x]==x ? x:(Top[x]=T(Top,Top[x]));
}//并查集,用来跳过已经松弛过的节点
void Dijkstra(int k) {//Dijkstra查询最短路Node X;int L,R,Nx,Ny,Len1,Len2;REP(i,1,n) {REP(j,1,m+1) {Dist[i][j]=INF;Top[i][j]=j;}}//初始化priority_queue<Node>Q;Q.push(Node(x[k],y[k],a[x[k]][y[k]]));Top[x[k]][y[k]]=y[k]+1;Dist[x[k]][y[k]]=0;//把第一个点压进队列,并把它的下一个设为设为它右边的点while(!Q.empty()) { //和正常的Dijkstra一样X=Q.top();Q.pop();//取出队头Len1=b[X.x][X.y];Nx=max(1,X.x-Len1);Ny=min(n,X.x+Len1);//设置X能弹射的范围REP(i,Nx,Ny) {Len2=Len1-abs(X.x-i);L=max(1,X.y-Len2);R=min(m,X.y+Len2);for(int j=T(Top[i],L); j<=R; j=T(Top[i],j)) {//并查集跳过已经松弛的点Q.push(Node(i,j,X.Dis+a[i][j]));//把可以跳到并且没有被松弛过的点压进队列Dist[i][j]=X.Dis;//松弛操作Top[i][j]=j+1;//把这个点的下一个设置为它右边的点}}}
}
int main() {Read(n);Read(m);REP(i,1,n) {REP(j,1,m) {Read(b[i][j]);}}REP(i,1,n) {REP(j,1,m) {Read(a[i][j]);}}REP(i,1,3) {Read(x[i]);Read(y[i]);}fill(Ans,Ans+3,0);Res=INF;REP(i,1,3) {Dijkstra(i);REP(j,1,3) {Ans[j]+=Dist[x[j]][y[j]]; //分别算出到每个点会合的最小代价}}REP(i,1,3) {if(Ans[i]<Res) {//打擂台找出最优解Res=Ans[i];Da=i;}}if(Res==INF) {//按题目要求输出答案puts("NO");} else {switch(Da) {case 1: {puts("X");break;}case 2: {puts("Y");break;}case 3: {puts("Z");break;}}Write(Res);}getchar();return flush(),0;
}

提交记录(小号)速度确实快了不少呢

这不算违规小号吧(逃)

转载于:https://www.cnblogs.com/zhudafu/p/9859945.html

题解 洛谷P4473 【[国家集训队]飞飞侠】相关推荐

  1. 洛谷P2619 [国家集训队]Tree I 题解

    洛谷P2619 [国家集训队]Tree I 题解 题目链接:P2619 [国家集训队]Tree I 题意: 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有 need\text{n ...

  2. 洛谷P1494 [国家集训队]小Z的袜子

    P1494 [国家集训队]小Z的袜子 题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- ...

  3. 洛谷 P4643 [国家集训队]阿狸和桃子的游戏

    题目:[国家集训队]阿狸和桃子的游戏 思路: 截个图,这个思路太巨了Orz. 图可以点.

  4. 洛谷P2839 [国家集训队]middle(主席树)

    P2839 [国家集训队]middle 我们可以考虑二分中位数 checkcheckcheck 答案,那么我们对于某个值 midmidmid ,把 [l,r][l,r][l,r] 内的所有小于 mid ...

  5. 洛谷P2619 [国家集训队2]Tree I(带权二分,Kruscal,归并排序)

    洛谷题目传送门 给一个比较有逼格的名词--WQS二分/带权二分/DP凸优化(当然这题不是DP). 用来解决一种特定类型的问题: 有\(n\)个物品,选择每一个都会有相应的权值,需要求出强制选\(nee ...

  6. 洛谷 P1903 [国家集训队]数颜色 / 维护队列

    题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2 ...

  7. [洛谷P1407][国家集训队]稳定婚姻

    题目大意:有$n$对夫妻和$m$对情人,如果一对情人中的两人都离婚了,那么他们可以结为夫妻.对于每一对夫妻,若他们离婚后所有人依然可以结婚,那么就是不安全的,否则是安全的.问每一对夫妻是否安全. 题解 ...

  8. 洛谷P2634 [国家集训队]聪聪可可(点分治)

    聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)--遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了 ...

  9. 洛谷P1494 [国家集训队]小Z的袜子 莫队

    题目描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编 ...

最新文章

  1. [bbk4957]第69集 第8章 -性能维护 00
  2. php随机生成域名,php生成短域名函数,php生成域名函数
  3. 硅谷Web 2.0时代即将结束? 实用型公司更受关注
  4. xunit-ICollectionFixture
  5. 聊聊基准测试的MVP方案
  6. linux redis 简书,Linux | Redis
  7. UVA10004 Bicoloring【DFS】
  8. 【python】dict4ini和xmltodict模块用途
  9. Ubuntu16.04 设置自启动脚本,系统重启自动执行自定义脚本任务
  10. react全局状态管理_Reto:在React中使用hooks管理全局状态
  11. Linux C++ UDP Socket(超详细)
  12. android开发,动态图标,Android动态更新APP图标
  13. 机器学习入门实例三——线性回归预测店铺销售额
  14. vue给标签动态添加元素_动态添加dom元素,并绑定vue事件
  15. “五一”或成疫情来最火爆假期,招行信用卡天天返利助力消费
  16. 群晖系统上的 Docker 使用拾遗
  17. 好玩的Ipaddian
  18. 反编译微信小程序(图文详细傻瓜式)
  19. 2018-9-25实验二
  20. 神经网络学习(三):解偏微分方程

热门文章

  1. 计算机学位论文的开题报告,计算机硕士论文开题报告格式范文
  2. unix哲学:集思广益的智慧(第一章 )
  3. 城商行牵手流量平台,开启信用卡合作新模式
  4. 使用Footprint Expert PRO 制作AD封装
  5. 盘点国外很很牛逼的安卓开源社区
  6. (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  7. python键盘怎么输入双引号_python中怎么输入引号 -问答-阿里云开发者社区-阿里云...
  8. C盘空间越来越小,怎么有效释放C盘空间
  9. 一图看懂什么是中小板、创业板、新三板、科创板
  10. 从高德地图看它的wmts服务