题解:P6403 [COCI2014-2015#2] STUDENTSKO

题面

[COCI2014-2015#2] STUDENTSKO

题目描述

一年一度的萨格勒布大学学生乒乓球团体赛将于下周六举行!每队由 \(k\) 名学生组成。\(n\) 个兴奋的学生们正在排队等候登记。Krešo 在登记处工作。他真的不想做他的工作,所以他决定不让学生选择团队。他决定第一组将由第一个排队的 \(k\) 名学生组成,第二组由下面的 \(k\) 名学生组成,第三组由后面的 \(k\) 名学生组成,以此类推。(\(n\equiv 0\pmod k\),因此没有人剩余。)

Ante 用一个整数来估计每个玩家的技能。他希望第一队拥有最弱的 \(k\) 个球员,第二队拥有第二弱的 \(k\) 个球员,依此类推,最后一队拥有最强的 \(k\) 个球员。

Krešo 刚刚休息了一下,Ante 决定转移排队的学生以达到自己的目标。他转移学生的方式是告诉一个学生从队列中走出来,在另一个学生后面排队,或者走到队列的前面。每转移一个学生花费一分钟。Krešo 有可能随时从休息中回来,所以 Ante 需要尽快实现他的目标。帮助 Ante 确定实现目标所需的最少分钟数。

输入格式

第一行输入包含整数 \(n\)\(k\),满足 \(n\bmod k=0\)

第二行包含 \(n\) 个空格分隔的整数 \(v_i\),表示第 \(i\) 个站在队列中的玩家技能的水平。

所有参赛者都有不同水平的技能。

输出格式

仅一行,即转移学生所用的最短分钟数。

样例 #1

样例输入 #1

4 1
9 12 5 13

样例输出 #1

1

样例 #2

样例输入 #2

6 2
16 2 1 7 5 10

样例输出 #2

1

样例 #3

样例输入 #3

6 3
7 9 8 3 6 5

样例输出 #3

3

提示

样例 3 说明

Ante 应该将技能等级为 \(5,6\)\(3\) 的学生移到队列的前面,花了三分钟。

数据范围与约定
  • 对于 \(30\%\) 的数据,有 \(1\le n\le 20\)
  • 对于 \(100\%\) 的数据,有 \(1\le n\le k\le 5\times 10^3\)

对于所有合法的 \(v_i\),都有 \(1\le v_i\le 10^9\)

说明

题目译自 COCI2014-2015 CONTEST #2 T3 STUDENTSKO

题解

思路

  1. 因为 Ante 希望第一队拥有最弱的 \(k\) 个球员,第二队拥有第二弱的 \(k\) 个球员,依此类推,最后一队拥有最强的 \(k\) 个球员。所以应先将球员按能力从小到大排序
  2. 此时球员的顺序是最理想的状态,将球员排序后,打上球队的标记,并还原球员的顺序。
  3. 因为原来的顺序不一定是最理想的顺序,所以要让移动次数最小,符合队伍排序的顺序最长。而这个符合队伍排序的顺序就是队伍编号的最长不降子序列

代码

蒟蒻还不会 $O\left ( n\log_{2}{n} \right ) $,只会 $O\left ( n^2 \right ) $。

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct stu {
	int a, id, team;
} s[5005];
int n, k;
bool cmp1(stu A, stu B) {
	return A.a < B.a;
}
bool cmp2(stu A, stu B) {
	return A.id < B.id;
}
int f() {//最长不降子序列,即符合队伍排序的顺序
	int dp[5005], ans = -1e9;
	for (int i = 1; i <= n; i++) {
		dp[i] = 1;
		for (int j = 1; j < i; j++)
			if (s[i].team >= s[j].team)
				dp[i] = max(dp[i], dp[j] + 1);
	}
	for (int i = 1; i <= n; i++)	ans = max(ans, dp[i]);
	return ans;
}
signed main() {
	cin.tie(0), cout.tie(0);
	cin >> n >> k;
	for (int i = 1; i <= n; i++)	cin >> s[i].a;//输入
	for (int i = 1; i <= n; i++)	s[i].id = i;//编号,为后面还原做标记
	sort(s + 1, s + 1 + n, cmp1);//按能力排序
	for (int i = 1; i <= n; i++)	s[i].team = (i + k - 1) / k;//打上队伍标号
	sort(s + 1, s + 1 + n, cmp2);//还原
	cout << n - f()<<"\n";
	return 0;
}
posted @ 2024-08-27 10:17  ggc114514  阅读(18)  评论(0)    收藏  举报