在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑
士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空
位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步
数完成任务。
首先分享一个我学习的博客文章:
Poj2449-A*初步+k短路
看着他的题解学会了k短路。。%%%
然后我就大致说一说k短路的求法吧。。
首先我们来看看A*。
A*,启发式搜索,是一种较为有效的搜索方法。
我们在搜索的时候,很多时候在当前状态,已经不是最优解了,但是我们却继续求解;
这个就是暴力搜索浪费时间的原因。
我们在有些时候,往往可以根据一些信息推断出继续搜索是一种劣解,
所以如果能够判断出来的话,就可以不继续了,以达到节省运行时间的目的。
来看一题很经典的例题:BZOJ 1085。
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑
士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空
位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步
数完成任务。
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑
士,*表示空位。两组数据之间没有空行。
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 29710 | Accepted: 8068 |
Description
Input
Output
Sample Input
2 2
1 2 5
2 1 4
1 2 2
Sample Output
14
这题待会儿要写题解,,算了还是写一遍吧(我帅)
题意就是:n个点,m条边,每条给出有向边并带有权值,给出start,end和k,求s~t所有路径中的第k短路。
没错这就是一道模板题目。。
我们来审视一下k短路的问题求解:
我们知道,可以暴力搜索,然后统计到第k短即可。
显然暴力是远远不行的。
另外,我们求最短路有许多优秀算法:dijkstra,SPFA……可以把最短路问题和k短路问题连接起来吗?
我们尝试一下A*。另f(x)是某k短路径的近似长度:
f(x)=g(x)+h(x)
f(x)就是路径的长度,g(x)是估价函数,我们选择x~end的最短路径(这个后面会解释);
h(x)是实际长度,start~x的总路径长。
那么我们优先访问f(x)更加小的点:因为它更可能成为最短,再第二短,再……
如果end被访问了k次了,那么目前得到的值f(x)就是k短路的长度。
现在可能还比较朦胧,我们进一步解释:
1.到底如何求k短路的?
我们考虑,要求k短路,要先求出最短路/次短路/第三短路……/第(k-1)短路,然后访问到第k短路。
接下来的方法就是如此操作的。
2.f(x)的意义?
我们得到的f(x)更小,优先访问这个f(x)的点。
我们可以定义一组数{p,g,h},p是某一个点,g是估价,h是实际,那么g+h更小的点p会优先访问。
为什么呢?因为假设我们求出了w短路,接下来要求(w+1)短路,就要求最小的另一条路径。
应该易理解。
3.为什么选择最短路来估价?
很简单的选择,我们既然要求最短了,当然是找最短路。
事实上这不是我们的初衷,但是有了“先求(k-1)短路”的概念后,这么理解也可以了。
4.实现
实现是比较简单的。
如何预处理出g(x)呢?显然,将所有边反向,然后求end到所有点的单源最短路径就好了。
接下来的启发式搜索可以简单解决。
事实上,就是在暴力搜索的基础上增加了启发式搜索:往一个最优的点的地方走。
另外还是要专来一篇这题blog的QAQ
关于上题目的程序:
(跑得有点慢。。300+ms。)
(大部分借鉴开头说的blog的程序)
//#include
#include
#include
#include
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int
Point=1005,
Edges=100005;
int n,m,start,end,kth;
int dist[Point],times[Point];
bool vis[Point];
struct Edge{
int to,next,val;
}E[Edges],Eopp[Edges]; //Eopp means Eopposite
int head[Point],head_opp[Point];
struct A_Star_node{
int p,g,h;
bool operator < (A_Star_node x)const{
return x.g+x.hQ;
inline void add(int Ecnt,int u,int v,int w){
E[Ecnt].next=head[u];
E[Ecnt].to=v;
E[Ecnt].val=w;
head[u]=Ecnt;
}
inline void add_opposite(int EoppCnt,int u,int v,int w){
Eopp[EoppCnt].next=head_opp[u];
Eopp[EoppCnt].to=v;
Eopp[EoppCnt].val=w;
head_opp[u]=EoppCnt;
}
void dijkstra(int s,int e){
memset(vis,0,sizeof(vis));
memset(dist,127,sizeof(dist));
int mini; dist[e]=0;
for (int i=1;i<=n;i++){
mini=0;
for (int j=1;j<=n;j++)
if (!vis[j] && dist[mini]>dist[j]) mini=j;
vis[mini]=1;
for (int x=head_opp[mini];x;x=Eopp[x].next)
dist[Eopp[x].to]=min(dist[Eopp[x].to],dist[mini]+Eopp[x].val);
}
}
int A_Star(int s,int e){
A_Star_node t1,tmp;
memset(times,0,sizeof(times));
t1.g=t1.h=0; t1.p=s;
Q.push(t1);
while (!Q.empty()){
t1=Q.top(); Q.pop();
times[t1.p]++;
if (times[t1.p]==kth && t1.p==e) return t1.h+t1.g;
if (times[t1.p]>kth) continue;
for (int i=head[t1.p];i;i=E[i].next){
tmp.p=E[i].to;
tmp.g=dist[E[i].to];
tmp.h=E[i].val+t1.h;
Q.push(tmp);
}
}
return -1;
}
int main(){
n=read(),m=read(),kth=read(),start=read(),end=read();
int x,y,z;
memset(head,0,sizeof(head));
memset(head_opp,0,sizeof(head_opp));
for (int i=1;i<=m;i++){
x=read(),y=read(),z=read();
add(i,x,y,z);
add_opposite(i,y,x,z);
}
dijkstra(start,end);
if (start==end) kth++;
int ans=A_Star(start,end);
if (ans==-1) puts("No");
else printf("%d\n",ans);
return 0;
}