P8230 地牢 题解
water_three · · 题解
update:
- 由于题意被更改补充,更改了一下做法,如果被卡常可以优化常数。感谢 @星空舞涵 指出错误。
- 又修改了一下小错误。
性质:
首先,通过读题我们可以得出以下性质:无论按怎样的顺序击败敌人,先击败能力值小的敌人的方案总是最优的。
因为在击败敌人后,能力值可能会大于击败前不能击败的敌人。又因为只有当击败敌人后才能通过该格子,所以需要将当前不能击败的记录下来,等到能击败时再搜索一遍。
做法:
我们写一个函数,搜索在起点范围里面能到达并击败的敌人,如果可以直接击败该敌人,直接击败。如果不行,就先存储。
存储完后,在优先队列中从小到大击败,顺便再搜索。如果不能击败此敌人,说明后面更大的敌人也不能击败,直接退出。
最后更新起点坐标。
代码:
#include<bits/stdc++.h>
using namespace std;
int l,n,m;
long long now=1;
vector<long long>a[1000001];
int endx,endy;
struct shu {
int value;
int x;
int y;
bool operator () (const shu &a,const shu &b) const {
return a.value>b.value;
}
};
priority_queue<shu,vector<shu>,shu >q;
int tot;
int find(int x,int y) { //搜索当前能到达的地方。
if(x<1||x>n||y<1||y>m||a[x][y]==-9||a[x][y]==-1||(endx==x&&endy==y))return 0;
if(a[x][y]!=0&&a[x][y]!=-9&&a[x][y]!=-1) {
if(now>=a[x][y])now+=a[x][y];//如果能击败,直接击败
else{
q.push({a[x][y],x,y});
a[x][y]=-9;
return 0;
}
}
a[x][y]=-9;//避免重复搜索
find(x+1,y);
find(x,y+1);
find(x-1,y);
find(x,y-1);
return 0;
}
int main() {
scanf("%d%d%d",&l,&n,&m);
int x=1,y=1;
while(l--) {
if(l==0){
endx=-1,endy=-1;
}
tot=0;
for(int i=1; i<=n; i++) {
a[i].clear();
a[i].push_back(0);
for(int j=1; j<=m; j++) {
int now;
cin>>now;
a[i].push_back(now);
if(a[i][j]==-1) {
endx=i,endy=j;
}
}
}
find(x,y);
while(!q.empty()) {
shu top=q.top();
if(top.value<=now) {
now+=top.value;
a[top.x][top.y]=0;
find(top.x,top.y);
q.pop();
} else {
break;
}
}
priority_queue<shu,vector<shu>,shu >qw;
q=qw;//注意清空队列
x=endx,y=endy;//更新起点
}
cout<<now<<endl;
}