题目描述
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第ii层楼(1 \le i \le N)(1≤i≤N)上有一个数字K_i(0 \le K_i \le N)Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3, 3 ,1 ,2 ,53,3,1,2,5代表了K_i(K_1=3,K_2=3,…)Ki(K1=3,K2=3,…),从11楼开始。在11楼,按“上”可以到44楼,按“下”是不起作用的,因为没有-2−2楼。那么,从AA楼到BB楼至少要按几次按钮呢?
输入输出格式
输入格式:
共二行。
第一行为33个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N)N,A,B(1≤N≤200,1≤A,B≤N)。
第二行为NN个用空格隔开的非负整数,表示K_iKi。
输出格式:
一行,即最少按键次数,若无法到达,则输出-1−1。
输入输出样例
输入样例#1: 复制
5 1 5 3 3 1 2 5
输出样例#1: 复制
3
ps:只有前两种是我自己写的,后面几种看不懂,先搬过来,以后在学,膜拜那些大佬orz
深搜记得秦伟剪枝,当当前的步数大于已经获得过的最短步数,就可以剪枝了
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int N,A,B,a[205],ans=inf;
bool vis[205];
void deepfs(int x,int t)
{
if(x<1||x>N||vis[x]||t>ans) return ;//轻微剪枝
if(x==B)
{
ans=min(t,ans);
return ;
}
vis[x]=1;
deepfs(x-a[x],t+1);
deepfs(x+a[x],t+1);
vis[x]=0;
}
int main()
{
cin>>N>>A>>B;
for(int i=1;i<=N;i++)
cin>>a[i];
deepfs(A,0);
if(ans==inf) cout<<"-1";
else cout<<ans;
return 0;
}
广搜:
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
struct point{ int x,step;};
int flo[205],N,A,B,ans=-1;
queue<point> Q;
bool vis[205] ;
point temp;
void buildfs()
{
vis[A]=1;
temp.x=A;
temp.step=0;
Q.push(temp);
while(!Q.empty())
{
point head=Q.front() ;
Q.pop();
if(head.x==B)
{
ans=head.step;
break;
}
int ix=head.x+flo[head.x];
if(ix<=N&&vis[ix]==false)
{
vis[ix]=true;
temp.x=ix;
temp.step=head.step+1;
Q.push(temp);
}
ix=head.x-flo[head.x];
if(ix>=1&&vis[ix]==false)
{
vis[ix]=true;
temp.x=ix;
temp.step=head.step+1;
Q.push(temp);
}
}
}
int main()
{
cin>>N>>A>>B;
for(int i=1;i<=N;i++)
cin>>flo[i];
buildfs();
cout<<ans;
return 0;
}
还有更高级的:floyed:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[201][201],n,a,b,x;
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
if(i!=j)
f[i][j]=400;
else f[i][j]=0;//初始化皆为不联通,自己到自己为0。
for(register int i=1;i<=n;++i)
{
scanf("%d",&x);
if(i+x<=n)f[i][i+x]=1;//if很重要。
if(i-x>0)f[i][i-x]=1;//把符合范围的i到i±x标记为做一次电梯即可到达。
}
for(register int k=1;k<=n;++k)
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
if(i!=j)f[i][j]=min(f[i][j],f[i][k]+f[k][j]);//裸的floyd不解释。
if(f[a][b]!=400)printf("%d",f[a][b]);
else printf("-1");//如果a到b无穷大,则永远到不了
return 0;
}
单源最短路spfa:
#include<bits/stdc++.h>
using namespace std;
const int N=210;
int n,a,b,tot,ver[2*N],head[N],next[2*N],dis[N];
bool vis[N];
queue<int> q;
void add(int x,int y)
{
ver[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
void spfa()
{
dis[a]=0;
vis[a]=1;
q.push(a);
while(q.size())
{
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=next[i])
{
int y=ver[i];
if(dis[y]>dis[x]+1)
{
dis[y]=dis[x]+1;
if(!vis[y])
{
q.push(y);
vis[y]=1;
}
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(i+x<=n) add(i,i+x);
if(i-x>=1) add(i,i-x);
}
memset(dis,0x3f3f3f3f,sizeof dis);
spfa();
if(dis[b]<0x3f3f3f3f) printf("%d",dis[b]);
else printf("-1");
return 0;
}
DP:
边界条件:f[a]=0,f[i]=INF(i≠a)
时间复杂度:O(n^2)
动态转移方程:if(v-k[v]>=1) f[v-k[v]]=min(f[v-k[v]],f[v]+1)
if(v+k[v]<=n) f[v+k[v]]=min(f[v+k[v]],f[v]+1)
但是这里如何判断不能到达呢?若不判断只能骗70分
我用了一个used数组,最初f[a]=1;
若新的状态更优,那么used[v+k[v]||v-k[v]]=1; 不停染色,若used[b]=0,则输出-1;其他情况输出f[b];
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
int f[201],k[201],n,a,b,used[201]={0};
int main()
{
memset(f,0x7777777,sizeof(f));
cin>>n>>a>>b;
for(int i=1;i<=n;i++)
cin>>k[i];
f[a]=0;used[a]=1;
for(int i=1;i<=n;i++){
for(int v=1;v<=n;v++)
{
if(v-k[v]>=1&&(f[v]+1)<=f[v-k[v]]) {
f[v-k[v]]=f[v]+1;
if(used[v]==1) used[v-k[v]]=1;
}
if(v+k[v]<=n&&(f[v]+1)<=f[v+k[v]]) {
f[v+k[v]]=f[v]+1;
if(used[v]==1) used[v+k[v]]=1;
}
}
}
if(used[b]==1) cout<<f[b]<<endl;
else cout<<"-1"<<endl;
return 0;
}