为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第三题-酒王
在线评测链接:P1268
题目内容
塔子哥和他的朋友们共 n 人是一群热爱生活的年轻人,他们经常在一起吃饭,聊天,玩游戏。有一天,他们决定去一家新开的酒吧,品尝各种美酒。但是他们发现,酒吧的老板是一个很奇怪的人,他给他们出了一个挑战:如果他们能在一个小时内喝完所有的酒,就可以免单;如果有人中途放弃,就要付双倍的钱。塔子哥和他的朋友们觉得这是一个很有趣的游戏,于是接受了挑战。
为了增加难度和乐趣,他们决定用一个特殊的方式来喝酒。他们顺时针围成一圈,假设标号为 1 到 n 。从 1 号开始,每次从当前的人顺时针数 k 个,然后这个人喝一杯酒。第 i 个人的酒量为意味着当他喝了
杯酒后将因无法忍受而离席。现在他们请你依次输出离席的人的编号,以此来判断谁是酒王。
输入描述
输入第一行为两个正整数 n,k 。
输入第二行为 n 个正整数,第 i 个数为 。
对于所有的数据: 。
输出描述
输出一行输出用空格隔开的 n 个正整数,表示按时间从早到晚离席的人的编号。
样例
输入
5 4 1 1 7 9 8
输出
1 5 2 4 3
思路
约瑟夫环问题+贪心
本题是经典的约瑟夫环问题变种。
从当前位置 cur 开始喝酒,下一个位置为顺时针的 cur + k 这个位置。由于这是一个圆,故一定会在一些人之间循环喝酒,一部分人在这段时间总是不会喝酒,我们称当前的情况为当前的酒局。喝酒的这部分人中,直到存在一个人喝完酒后,剩余酒杯数为 0 ,这一部分人的喝酒结束,整个酒局将被修改。
我们需要考虑,当前的喝酒的人中,从当前位置 cur 开始,一直顺时针 k次找下一个人,而离席的第一个人,必然是酒杯数最少的。
由于存在先后关系,从 cur 开始顺时针 k 次找下一个喝酒的人,这些人中,剩余酒杯数最少,且是所有剩余酒杯数最少的人中第一个喝酒的人,是第一个离席的人。这个人前面的人会和他和一样多的酒,后面的人会比他少喝一杯酒。
这个人离席后,其顺时针后面的人就接上其位置,进行下一次的酒局。
时间复杂度:
视频实况 v1, 21:12-65:56
类似题目推荐
LeetCode
剑指 Offer 62. 圆圈中最后剩下的数字
CodeFun2000
P1118 2023.03.26-阿里春招-第二题-报数字
代码
CPP
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
pair<int, int> a[N];
int ans[N], g;
int vis[N];
int people[N];
int n, k;
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; ++i) scanf("%d", &a[i].first), a[i].second = i + 1;
int cur = 0;
while (n > 0) {
for (int i = 0; i < n; ++i) vis[i] = 0;
int cnt = 0;
int minv = 0x3f3f3f3f;
// 找到当前回合所有可能需要喝酒的人
while (!vis[cur]) {
people[cnt++] = cur;
vis[cur] = 1;
minv = min(minv, a[cur].first);
cur = (cur + k) % n;
}
// 我们找到从 cur 开始的最小值
// cur 之前的人要减去 minv
// cur 之后的人要减去 minv - 1
for (int i = 0; i < cnt; ++i)
if (minv == a[people[i]].first) {
for (int j = 0; j < i; ++j) a[people[j]].first -= minv;
for (int j = i + 1; j < cnt; ++j) a[people[j]].first -= minv - 1;
// 将其排列好,然后再往后走 k 个
int pos = people[i];
ans[++g] = a[pos].second;
for (int j = pos + 1; j < n; ++j) a[j - 1] = a[j];
if (n > 1) {
// 走 k - 1步是因为下一个人继承了当前离席的人的位置,故从当前离席的顺时针走 k 个
cur = (pos + k - 1) % (n - 1);
}
break;
}
n -= 1;
}
for (int i = 1; i <= g; ++i) printf("%d%c", ans[i], " \n"[i == g]);
return 0;
}
python
N = 1010 n, k = map(int, input().split()) a = [[0, 0] for i in range(N)] vis = [0] * N line = input().split() for i in range(len(line)): a[i] = ([int(line[i]), i + 1]) cur = 0 ans = [] while n > 0: for i in range(n): vis[i] = 0 people = [] minv = 0x3f3f3f3f # 找到当前回合所有可能需要喝酒的人 while vis[cur] == 0: people.append(cur) vis[cur] = 1 minv = min(minv, a[cur][0]) cur = (cur + k) % n # 我们找到从 cur 开始的最小值 # cur 之前的人要减去 minv # cur 之后的人要减去 minv - 1 for i in range(len(people)): if minv == a[people[i]][0]: for j in range(i): a[people[j]][0] -= minv for j in range(i + 1, len(people)): a[people[j]][0] -= minv - 1 # 将其排列好,然后再往后走 k 个 pos = people[i] ans.append(a[pos][1]) a.pop(pos) if n > 1: # 走 k - 1步是因为下一个人继承了当前离席的人的位置,故从当前离席的顺时针走 k 个 cur = (pos + k - 1) % (n - 1) break n -= 1 for i in ans: print(i, end=' ') print()
Java
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static final int N = 1010;
static int[] ans = new int[N], people = new int[N];
static int[] vis = new int[N];
static int[][] a = new int[N][2];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
for (int i = 0; i < n; ++i) {
a[i][0] = sc.nextInt();
a[i][1] = i + 1;
}
int g = 0;
int cur = 0;
while (n > 0) {
for (int i = 0; i < n; ++i) vis[i] = 0;
int cnt = 0;
int minv = 0x3f3f3f3f;
// 找到当前回合所有可能需要喝酒的人
while (vis[cur] == 0) {
people[cnt++] = cur;
vis[cur] = 1;
minv = Math.min(minv, a[cur][0]);
cur = (cur + k) % n;
}
// 我们找到从 cur 开始的最小值
// cur 之前的人要减去 minv
// cur 之后的人要减去 minv - 1
for (int i = 0; i < cnt; ++i) {
if (minv == a[people[i]][0]) {
for (int j = 0; j < i; ++j) {
a[people[j]][0] -= minv;
}
for (int j = i + 1; j < cnt; ++j) {
if (j != i) {
a[people[j]][0] -= minv - 1;
}
}
// 将其排列好,然后再往后走 k 个
int pos = people[i];
ans[++g] = a[pos][1];
for (int j = pos + 1; j < n; ++j) {
a[j - 1] = a[j];
}
// 走 k - 1步是因为下一个人继承了当前离席的人的位置,故从当前离席的顺时针走 k 个
if (n > 1) {
cur = (pos + k - 1) % (n - 1);
}
break;
}
}
n -= 1;
}
for (int i = 1; i <= g; ++i) {
System.out.print(ans[i] + " ");
}
System.out.println();
}
}
Go
package main
import "fmt"
type Pair struct {
first, second int
}
const N = 1010
var a [N]Pair
var ans [N]int
var vis [N]bool
var people [N]int
var g int
var n, k int
func main() {
fmt.Scan(&n, &k)
for i := 0; i < n; i++ {
fmt.Scan(&a[i].first)
a[i].second = i + 1
}
cur := 0
for n > 0 {
for i := 0; i < n; i++ {
vis[i] = false
}
cnt := 0
minv := 0x3f3f3f3f
// 找到当前回合所有可能需要喝酒的人
for !vis[cur] {
people[cnt] = cur
vis[cur] = true
minv = min(minv, a[cur].first)
cur = (cur + k) % n
cnt++
}
// 我们找到从 cur 开始的最小值
// cur 之前的人要减去 minv
// cur 之后的人要减去 minv - 1
for i := 0; i < cnt; i++ {
if minv == a[people[i]].first {
for j := 0; j < i; j++ {
a[people[j]].first -= minv
}
for j := i + 1; j < cnt; j++ {
a[people[j]].first -= minv - 1
}
// 将其排列好,然后再往后走 k 个
pos := people[i]
ans[g+1] = a[pos].second
g++
for j := pos + 1; j < n; j++ {
a[j-1] = a[j]
}
if n > 1 {
// 走 k - 1步是因为下一个人继承了当前离席的人的位置,故从当前离席的顺时针走 k 个
cur = (pos + k - 1) % (n - 1)
}
break
}
}
n--
}
for i := 1; i <= g; i++ {
if i == g {
fmt.Printf("%d\n", ans[i])
} else {
fmt.Printf("%d ", ans[i])
}
}
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
Js
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
const lines = input.trim().split('\n');
[n, k] = lines[0].split(' ').map(Number);
const a = lines[1].split(' ').map((x, i) => [Number(x), i + 1]);
const people = new Array(n);
const ans = new Array(n);
let cur = 0;
let g = 0;
while (n > 0) {
const vis = new Array(n).fill(false);
let cnt = 0;
let minv = Infinity;
// 找到当前回合所有可能需要喝酒的人
while (!vis[cur]) {
people[cnt++] = cur;
vis[cur] = true;
minv = Math.min(minv, a[cur][0]);
cur = (cur + k) % n;
}
// 我们找到从 cur 开始的最小值
// cur 之前的人要减去 minv
// cur 之后的人要减去 minv - 1
for (let i = 0; i < cnt; i++) {
const p = people[i];
if (minv === a[p][0]) {
for (let j = 0; j < i; j++) {
a[people[j]][0] -= minv;
}
for (let j = i + 1; j < cnt; j++) {
a[people[j]][0] -= minv - 1;
}
// 将其排列好,然后再往后走 k 个
const pos = p;
ans[g++] = a[pos][1];
for (let j = pos + 1; j < n; j++) {
a[j - 1] = a[j];
}
if (n > 1) {
// 走 k - 1 步是因为下一个人继承了当前离席的人的位置,故从当前离席的顺时针走 k 个
cur = (pos + k - 1) % (n - 1);
}
break;
}
}
n--;
}
console.log(ans.join(' '));
});

















