首页 > 试题广场 >

太阳系DISCO

[编程题]太阳系DISCO
  • 热度指数:845 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
{\hspace{15pt}}在一个平行世界的太阳系中,所有行星恰好构成一个长度为 n,按顺时针依次编号为 1,2,\dots ,n。相邻两颗行星间距离相等,且保证 n 为偶数。

{\hspace{15pt}}你位于编号为 a 的行星,目标是到达编号为 b 的行星。你可以执行以下三种操作,每次操作均消耗 1 个单位时间:
{\hspace{23pt}}\bullet\,顺时针移动 x 颗行星;
{\hspace{23pt}}\bullet\,逆时针移动 y 颗行星;
{\hspace{23pt}}\bullet\,发动一次传送技能(最多可使用 k 次),将你顺时针移动 \tfrac{n}{2} 颗行星,即跳到正对面的那颗行星。

{\hspace{15pt}}请你计算,从 a 行星移动到 b 行星的最少时间;若无论如何都无法到达,则输出 -1

输入描述:
{\hspace{15pt}}在一行上输入 6 个整数 n,k,a,b,x,y,含义分别为:
{\hspace{23pt}}\bullet\,n\left(2 \leqq n \leqq 2\times 10^{5}\right)——行星数量,且 n 为偶数;
{\hspace{23pt}}\bullet\,k\left(0 \leqq k \leqq 2\times 10^{5}\right)——技能可使用的最大次数;
{\hspace{23pt}}\bullet\,a,b\left(1 \leqq a,b \leqq n\right)——起点与终点的编号;
{\hspace{23pt}}\bullet\,x,y\left(1 \leqq x,y \leqq n\right)——每次普通移动的距离。


输出描述:
{\hspace{15pt}}输出一个整数,表示最少所需时间;若无法到达,则输出 -1
示例1

输入

4 0 1 2 2 1

输出

2

说明

你可以先顺时针移动 x=2 颗行星到达编号 3,再逆时针移动 y=1 颗行星到达编号 2,共耗时 2
示例2

输入

4 114514 1 3 1 1

输出

1
示例3

输入

4 114514 1 2 2 2

输出

-1

备注:

广度优先搜索,a到b等价于0到(b-a + n)%n,并且注意到传送2次等于没有传送,所以分没有传送和传送1次两种情况考虑,没有传送时,求出从0出发到1、2.....n-1最小次数所组成的数组s,对于有传送的次数数组ss,ss[k] = s[(k + n/2)%n ]+1


#include <iostream>
#include <queue>
#include <variant>
#include <vector>
using namespace std;

int main() {
    int n, k, a, b, x, y;
    cin >> n >> k >> a >> b >> x >> y;

    int nostar = (b - a + n) % n;

    vector<int>vec_moves(n, -1);
   
    vec_moves[0] = 0;

    queue<int>qe;
    qe.push(0);
    int nstep = 0;
    while (!qe.empty()) {
        int nsize = qe.size();
        nstep++;
        for (int i = 0; i < nsize; i++) {
            int nval = qe.front();
            qe.pop();
            int mx = (nval + x + n) % n;
            int my = (nval - y + n) % n;
            if (vec_moves[mx] == -1) {
                vec_moves[mx] = nstep;
                qe.push(mx);
            }
            if (vec_moves[my] == -1) {
                vec_moves[my] = nstep;
                qe.push(my);
            }
        }
    }

    vector<int>vec_moves_trans(n, -1);
    for (int i = 0; i < n; i++) {
        int j = (i + n / 2) % n;
        if (vec_moves[j] != -1) {
            vec_moves_trans[i] = vec_moves[j] + 1;
        } else {
            vec_moves_trans[i] = -1;
        }

    }

    int nminstep;

    if (k == 0) {
        nminstep = vec_moves[nostar];
    } else {
        if (vec_moves[nostar] == -1 && vec_moves_trans[nostar] == -1) {
            nminstep = -1;
        } else if (vec_moves[nostar] != -1 && vec_moves_trans[nostar] == -1) {
            nminstep = vec_moves[nostar];

        } else if (vec_moves[nostar] == -1 && vec_moves_trans[nostar] != -1) {
            nminstep = vec_moves_trans[nostar];
        } else {
            nminstep = vec_moves[nostar] < vec_moves_trans[nostar] ? vec_moves[nostar] :
                       vec_moves_trans[nostar];
        }
    }
    cout << nminstep;
}
// 64 位输出请用 printf("%lld")
编辑于 2025-10-23 19:24:05 回复(0)
BFS搜索完所有路径并记录最小时间即可,C语言要自己创建队列,恶心
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
//就是bfs,然后遍历所有位置就行了吧,加上步数计数,把那个环看成一个图
//广度优先搜索需要队列,c语言要自己创建队列恶心
typedef struct{
    int bianhao; //所到达星球的编号
    int time_count;// 到达这个位置花费的单位时间
    int chuansong; //到达这个位置所剩余的传送次数
}pos;

#define N (int)200000

pos queue[N];
int queue_front=0;
int queue_end=0;

//判断是否空队列
bool is_empty_queue()
{
    if(queue_front==queue_end)
    {return true;}
    return false;
}

//入队,循环队列
bool queue_push(pos temp)
{
    if(((queue_end+1)%N)==queue_front) //队列满了
    {return false;}
    queue[queue_end]=temp;
    
    queue_end=(queue_end+1)%N;//求余回到队头
    return true;
}

//出队
pos queue_pop()
{
    pos temp=queue[queue_front];
    queue_front=(queue_front+1)%N;
    return temp;
}

//清空队列
void clear_queue(){
    queue_front=0;
    queue_end=0;
}

int n,//行星数量,且 n 为偶数
    k, //技能可使用的最大次数
    a,b,//起点与终点的编号
    x,y;//每次普通移动的距离
int *stars;  //行星环
int min_time;  //到达终点所花的最少时间
int B;  //终点的编号
//bfs遍历
void bfs()
{
    while (!is_empty_queue()) {  //队列不为空时取出队列
        pos temp=queue_pop();

        if (temp.bianhao==b) { //是否到达终点,结束标记
            if(temp.time_count<min_time)  //到达终点的时间是否最少
            {
                min_time=temp.time_count;
            }
            return;
        }

        //往三种操作探索,每次操作均消耗1 个单位时间
        if (stars[(temp.bianhao+x)%n]==0) {     //顺时针移动 x 颗行星
            stars[(temp.bianhao+x)%n]=1;//阻塞这个位置

            //把这个位置加入队列
            pos t={
                .bianhao=(temp.bianhao+x)%n,
                .chuansong=temp.chuansong,
                .time_count=temp.time_count+1
            };
            queue_push(t);    
        }
        if (stars[(temp.bianhao+(n-y))%n]==0) {     //逆时针移动 y 颗行星,相当于顺时针移动(n-y)颗星
            stars[(temp.bianhao+(n-y))%n]=1;//阻塞这个位置

            //把这个位置加入队列
            pos t={
                .bianhao=(temp.bianhao+(n-y))%n,
                .chuansong=temp.chuansong,
                .time_count=temp.time_count+1
            };
            queue_push(t);    
        }
        if(stars[(temp.bianhao+(n/2))%n]==0&&temp.chuansong>=1)  //传送技能,顺时针移动 n/2颗行星,  需要有传送技能才能发动传送
        {
            stars[(temp.bianhao+(n/2))%n]=1;//阻塞这个位置
            //把这个位置加入队列
            pos t={
                .bianhao=(temp.bianhao+(n/2))%n,
                .chuansong=temp.chuansong-1,  //注意传送次数减1
                .time_count=temp.time_count+1
            };
            queue_push(t);
        }
    }
}

int main() {
    
    scanf("%d %d %d %d %d %d",&n,&k,&a,&b,&x,&y);

    min_time=n+1; //初始化最少时间
    //初始化行星地图,0元素不要,数值为0表示这个位置还没有到达过
    stars=malloc(sizeof(int)*(n+1));
    memset(stars,0,sizeof(int)*(n+1));

    //起点进入队列然后开始bfs遍历
    pos start_pos={
        .bianhao=a,
        .time_count=0,
        .chuansong=k
    };
    //入队列
    queue_push(start_pos);
    //阻塞这个位置
    stars[start_pos.bianhao]=1;
    bfs();
    if (min_time>n) {
        printf("-1");  //无法到达终点
    }else {
        printf("%d",min_time);
    }

    return 0;
}


发表于 2025-10-18 12:28:23 回复(0)
package main

import (
	"bufio"
	"fmt"
	"os"
)

/* 
    BFS
    visited[pos][tp] 表示在位置 pos,使用 tp 次传送的状态是否访问过
	tp 只有 0 和 1 两种状态(因为用两次等于没用)
    queue := []State
 */

type State struct {
	pos   int // 当前位置 (0 ~ n-1)
	steps int // 已用步数
	tp    int // 是否使用过传送: 0 或 1
}

func minSteps(n, k, a, b, x, y int) int {
	a-- // 转为 0-indexed
	b-- // 转为 0-indexed

	if a == b {
		return 0
	}

	// visited[pos][tp] 表示在位置 pos,使用 tp 次传送的状态是否访问过
	// tp 只有 0 和 1 两种状态(因为用两次等于没用)
	visited := make([][]bool, n)
	for i := range visited {
		visited[i] = make([]bool, 2) // tp: 0 或 1
	}

	// BFS 队列
	queue := []State{{a, 0, 0}}
	visited[a][0] = true

	for len(queue) > 0 {
		cur := queue[0]
		queue = queue[1:] // 出队

		nextSteps := cur.steps + 1

		// 1. 顺时针移动 x 步
		p1 := (cur.pos + x) % n
		if !visited[p1][cur.tp] {
			if p1 == b {
				return nextSteps
			}
			visited[p1][cur.tp] = true
			queue = append(queue, State{p1, nextSteps, cur.tp})
		}

		// 2. 逆时针移动 y 步
		p2 := (cur.pos - y + n) % n // +n 防止负数
		if !visited[p2][cur.tp] {
			if p2 == b {
				return nextSteps
			}
			visited[p2][cur.tp] = true
			queue = append(queue, State{p2, nextSteps, cur.tp})
		}

		// 3. 使用传送技能(跳到对面)
		if cur.tp == 0 && k >= 1 { // 还没用过,且 k >= 1
			p3 := (cur.pos + n/2) % n
			if !visited[p3][1] {
				if p3 == b {
					return nextSteps
				}
				visited[p3][1] = true
				queue = append(queue, State{p3, nextSteps, 1})
			}
		}
	}

	return -1
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	var n, k, a, b, x, y int
	_, err := fmt.Fscan(reader, &n, &k, &a, &b, &x, &y)
	if err != nil {
		panic(err)
	}

	result := minSteps(n, k, a, b, x, y)
	fmt.Println(result)
}

发表于 2025-09-07 16:40:30 回复(0)
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;

int safe_mod(int a, int b) {
    return (a % b + b) % b;
}

int main() {
    int n, k, a, b, x, y;
    cin >> n >> k >> a >> b >> x >> y;
    
    // 定义移动方式
    vector<int> moves = {+x, -y};
    if (k > 0) {
        moves.push_back(+n/2);  // 只有k>0时才允许使用第三种移动
    }
    
    queue<int> q;
    vector<bool> visited(n, false);
    
    if(a == b) {
        cout << 0 << endl;
        return 0;
    }
    
    q.push(a);
    visited[a] = true;
    int steps = 0;
    bool found = false;
    
    while(!q.empty() && steps <= k + n) {  // 合理设置上限
        int sz = q.size();
        while(sz--) {
            int current = q.front();
            q.pop();
            
            for(int move : moves) {
                int next = safe_mod(current + move, n);
                
                if(next == b) {
                    cout << steps + 1 << endl;
                    return 0;
                }
                
                if(!visited[next]) {
                    visited[next] = true;
                    q.push(next);
                }
            }
        }
        steps++;
    }
    
    cout << -1 << endl;
    return 0;
}

发表于 2025-08-29 23:09:24 回复(0)
public static int solve(int size, int k, int start, int end, int shun, int ni) {
        //记录该点是否访问过,0表示未访问,1表示访问过
        int[] nums = new int[size + 1];
        //广搜
        ArrayDeque<Integer> queue = new ArrayDeque<>();
        queue.add(start);
        int step = 0;
        while (!queue.isEmpty()) {
            int queueSize = queue.size();
            for (int i = 0; i < queueSize; i++) {
                int node = queue.remove();
                //该点访问过,直接跳过
                if (nums[node] == 1) {
                    continue;
                } else {
                    //该点之前没有访问过,现在访问了,置为1
                    nums[node] = 1;
                }
                //判断是否为终点
                if (node == end) {
                    return step;
                } else {
                    //访问顺时针能到的行星
                    queue.add((node + shun) % size);
                    //访问逆时针能到的行星
                    queue.add((node - ni) == 0 ? size : (node - ni + size) % size);

                    //如果还有传输次数,并且传送目标行星没有被访问过,访问传送能到达的行星
                    if (k > 0 && nums[(node + size / 2) % size] != 1) {
                        queue.add((node + size / 2) % size);
                        k--;
                    }
                }
            }
            //每个行星能访问到的其他3个行星入队后,用时加1
            step++;
        }
        //访问不到,返回-1
        return -1;
    }

发表于 2025-08-26 21:16:14 回复(0)
广搜
发表于 2025-08-01 19:44:32 回复(0)
from collections import deque

def min_steps(n, k, a, b, x, y):
    a -= 1
    b -= 1

    if a == b:
        return 0

    visited = [[False]*2 for _ in range(n)]
    q = deque()
    q.append((a, 0, 0))  # (当前位置, 步数, teleport_used (0&nbs***bsp;1))
    visited[a][0] = True

    while q:
        pos, steps, tp = q.popleft()

        # 顺时针 x
        p1 = (pos + x) % n
        if not visited[p1][tp]:
            if p1 == b:
                return steps + 1
            visited[p1][tp] = True
            q.append((p1, steps + 1, tp))

        # 逆时针 y
        p2 = (pos - y + n) % n
        if not visited[p2][tp]:
            if p2 == b:
                return steps + 1
            visited[p2][tp] = True
            q.append((p2, steps + 1, tp))

        # 传送,两次及以上就没有意义
        if tp == 0 and k >= 1:
            p3 = (pos + n // 2) % n
            if not visited[p3][1]:
                if p3 == b:
                    return steps + 1
                visited[p3][1] = True
                q.append((p3, steps + 1, 1))

    return -1

# 输入读取
n, k, a, b, x, y = map(int, input().split())
print(min_steps(n, k, a, b, x, y))

发表于 2025-07-29 09:38:03 回复(1)