洛谷P1135 奇怪的电梯(5种解法)

题目描述

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第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;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值