题解 P2559 【[AHOI2002]哈利·波特与魔法石】
Ricardo_21 · · 题解
分析一下题目 其实是一个图。但,实话实说,对于我们这种蒟蒻来说,这种图还是比较难的
首先要生成这个图的各个路径,显然路径的长度已经给了,而且能不能有魔法石(能不能开开挂)也已经告诉我们了
也就是说!!!
这个图已经出来了,边,点的信息都有了
所以,直接开始暴搜
但是要记住,如果i可以到j,那么j也可以到i
#include<bits/stdc++.h>
using namespace std;
int h[8]={0,2,6,4,8,6,10,14};
int s[8];
int start,final;
int n,tx,ty,a,maxn=-1,minn=100000000;
int dis[110][110];
int vis[110][110];
void dfs(int x,int t){
if(x==final){
minn=min(minn,t);
return;
}
for(int i=1;i<=maxn;i++){
if(vis[x][i]!=0){
if(dis[x][i]) dfs(i,t+h[vis[x][i]]/2);
else dfs(i,t+h[vis[x][i]]);
}
}
}
int main(){
for(int i=1;i<=7;i++)
cin>>s[i];
cin>>start>>final>>n;
for(int i=1;i<=n;i++){
cin>>tx>>ty>>a;
vis[tx][ty]=a;
vis[ty][tx]=a;
dis[tx][ty]=s[a];
dis[ty][tx]=s[a];
maxn=max(maxn,max(tx,ty));
}
dfs(start,0);
cout<<minn<<endl;
return 0;
}
但是。。。。。。
你会发现这题会MLE+WA,只有10分,在DFS时,系统栈会爆掉
然而下面的程序没有将“如果i可以到j,那么j也可以到i”加到程序里竟然会20分!!!
不得不说,数据太水。。。
#include<bits/stdc++.h>
using namespace std;
int h[8]={0,2,6,4,8,6,10,14};
int s[8];
int start,final;
int n,tx,ty,a,maxn=-1,minn=0x3f3f3f3f3f3f;
int dis[110][110];
int vis[110][110];
void dfs(int x,int m){
if(x==final){
minn=min(minn,m);
return;
}
for(int i=1;i<=final;i++){
if(vis[x][i]!=0){
if(dis[x][i]) dfs(i,m+h[vis[x][i]]/2);
else dfs(i,m+h[vis[x][i]]);
}
}
}
int main(){
for(int i=1;i<=7;i++)
cin>>s[i];
cin>>start>>final>>n;
for(int i=1;i<=n;i++){
cin>>tx>>ty>>a;
vis[tx][ty]=a;
dis[tx][ty]=s[a];
//maxn=max(maxn,max(tx,ty));
}
dfs(start,0);
cout<<minn<<endl;
return 0;
}
再仔细分析,发现,系统栈,好像受不住
所以,我们开始一些搜索剪枝
相信许多人做过迷宫这类的题目,会想到用走一遍就打一个标记,再回溯的方法。但是在这一题,显然是不大奏效的
所以另外一个剪枝方法便很明显了
记忆化
记忆化搜索:算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存。
记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,以后再次遇到这个状态的时候,就不必重新求解了。
————摘自360百科
(搜索基本上都要挂上记忆化,我将记忆化理解为搜索剪枝的必由之路)
所以如何写记忆化呢?
首先,我们发现。你会“倚门回首”。
“倚门回首”:你会发现自己在搜索的途中莫名回头,由i到j之后,再从j到i,从而将一个好好的系统栈直接MLE掉
如何解决?
开一个f数组,存放到当前位置的最小距离即可
如果f[x]小于t那么直接return,把这个分支毙掉
否则将f[x](x表示当前位置)赋值为t(距离)
所以便显而易见了
#include<bits/stdc++.h>
using namespace std;
int h[8]={0,2,6,4,8,6,10,14};
int s[8];
int start,final;
int n,tx,ty,a,maxn=-1,minn=100000000;
int dis[110][110];
int vis[110][110];
int f[110];
void dfs(int x,int t){
if(t>f[x]) return;
f[x]=t;
if(x==final){
minn=min(minn,t);
return;
}
for(int i=1;i<=maxn;i++){
if(vis[x][i]!=0){
if(dis[x][i]) dfs(i,t+h[vis[x][i]]/2);
else dfs(i,t+h[vis[x][i]]);
}
}
}
int main(){
for(int i=1;i<=100;i++) f[i]=100000000;
for(int i=1;i<=7;i++)
cin>>s[i];
cin>>start>>final>>n;
for(int i=1;i<=n;i++){
cin>>tx>>ty>>a;
vis[tx][ty]=a;
vis[ty][tx]=a;
dis[tx][ty]=s[a];
dis[ty][tx]=s[a];
maxn=max(maxn,max(tx,ty));
}
dfs(start,0);
cout<<minn<<endl;
return 0;
}
(只加了四行。。。就AC了)
CSP-J RP++