刺杀大使
题目描述
某组织正在策划一起对某大使的刺杀行动。他们来到了使馆,准备完成此次刺杀,要进入使馆首先必须通过使馆前的防御迷阵。
迷阵由 n × m n\times m n×m 个相同的小房间组成,每个房间与相邻四个房间之间有门可通行。在第 n n n 行的 m m m 个房间里有 m m m 个机关,这些机关必须全部打开才可以进入大使馆。而第 1 1 1 行的 m m m 个房间有 m m m 扇向外打开的门,是迷阵的入口。除了第 1 1 1 行和第 n n n 行的房间外,每个房间都被使馆的安保人员安装了激光杀伤装置,将会对进入房间的人造成一定的伤害。第 i i i 行第 j j j 列 造成的伤害值为 p i , j p_{i,j} pi,j(第 1 1 1 行和第 n n n 行的 p p p 值全部为 0 0 0)。
现在某组织打算以最小伤害代价进入迷阵,打开全部机关,显然,他们可以选 择任意多的人从任意的门进入,但必须到达第 n n n 行的每个房间。一个士兵受到的伤害值为他到达某个机关的路径上所有房间的伤害值中的最大值,整个部队受到的伤害值为所有士兵的伤害值中的最大值。现在,这个恐怖组织掌握了迷阵的情况,他们需要提前知道怎么安排士兵的行进路线可以使得整个部队的伤害值最小。
输入格式
第一行有两个整数 n , m n,m n,m,表示迷阵的大小。
接下来 n n n 行,每行 m m m 个数,第 i i i 行第 j j j 列的数表示 p i , j p_{i,j} pi,j。
输出格式
输出一个数,表示最小伤害代价。
样例 #1
样例输入 #1
4 2
0 0
3 5
2 4
0 0
样例输出 #1
3
#include <bits/stdc++.h>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%d",&x);
typedef long long ll;
const int N=1e3+10,mod=100003;
int a[N][N];
bool st[N][N];
int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};
int n,m,ans;
struct E{
int x,y,u;
};//原本我想1用优先队列把伤害小的放前面 2调整st数组在出队时进行标记(更新权重时的写法)
//但是这个会超时,于是又改回了基本的BFS搜索方式
queue<E>q;
bool fun(int len){//从第二行往上找一条路径,它上的点均不超过len
while(!q.empty()) q.pop();
memset(st,0,sizeof(st));
for(int i=1;i<=m;i++){//把倒数第二行满足的点压入
if(a[n-1][i]<=len) q.push(E{n-1,i,a[n-1][i]});
}
E nw,nx;
int x,y;
int l,r;
int f=0;
while(!q.empty()){
nw=q.front();q.pop();
x=nw.x;y=nw.y;
if(st[x][y]==1) continue;
st[x][y]=1;
if(x==1) {f=1;break;}//如果到达了第一行,说明此时是可以的
for(int i=0;i<4;i++){
l=x+dx[i];
r=y+dy[i];
if(a[l][r]>len) continue;//如果不满足值大于此处的len,则不可以继续
if(l<1||r<1||l>n||r>m) continue;
if(st[l][r]) continue;
q.push(E{l,r,a[l][r]});
}
}
if(f==1) return 1;
return 0;
}
int main(){
cin>>n>>m;
int a_max=-1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sf(a[i][j])
if(a[i][j]>a_max){
a_max=a[i][j];
}
}
}
int l=1,r=a_max+1,mid;
while(l<r){
mid=(l+r)/2;
if(fun(mid)) r=mid;//这里包含了:1找到的路径伤害值恰好等于mid 2伤害值比mid小
else l=mid+1;
}
de(l)
return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");//做法二:Kruakal
#define sf(x) scanf("%d",&x);
typedef long long ll;
const int N=1e3+10,M=2e6+10,mod=100003;
int a[N][N];
int f[M];
int n,m,ans;
struct E{
int x,y,u;
}e[M];
int cn=0;
int BIAO(int x,int y){//给每个点标号
return (x-1)*m+y;
}
int find(int x){
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
bool cmp(E a,E b){
return a.u<b.u;
}
void kruskal(){
sort(e+1,e+cn+1,cmp);//默认按照第一个u(伤害值)排序
// for(int i=1;i<=cn;i++){
// de(666)de(e[i].u)de(666)Pu
// }
int a,b;
int st=BIAO(1,1);//由于第一行和最后一行一定是最先联通的,它们的权重都为0
int ed=BIAO(n,m);
for(int i=1;i<=cn;i++){
a=find(e[i].x);//找这两个点对应的父亲
b=find(e[i].y);
if(a==b) continue;
if(a>b) f[a]=b;//合并父亲
else f[b]=a;
ans=max(ans,e[i].u);
//注意这里需要调用find函数,因为中途有可能有一些点没有更新
if(find(st)==find(ed)) return ;//此时已经联通
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sf(a[i][j])
f[BIAO(i,j)]=BIAO(i,j);//初始化父亲
}
}
int u,v1,v2;//建立图的过程,参考的题解
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
u=BIAO(i,j);
v1=BIAO(i,j+1);
v2=BIAO(i+1,j);
if(j+1<=m)e[++cn]={u,v1,max(a[i][j],a[i][j+1])};
if(i+1<=n)e[++cn]={u,v2,max(a[i][j],a[i+1][j])};
}
}
kruskal();
de(ans)
return 0;
}