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

题面

[COCI2014-2015#2] STUDENTSKO

题目描述

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

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

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

输入格式

第一行输入包含整数 n n n k k k,满足 n   m o d   k = 0 n\bmod k=0 nmodk=0

第二行包含 n n n 个空格分隔的整数 v i v_i vi,表示第 i 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 5,6 5,6 3 3 3 的学生移到队列的前面,花了三分钟。

数据范围与约定
  • 对于 30 % 30\% 30% 的数据,有 1 ≤ n ≤ 20 1\le n\le 20 1n20
  • 对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ k ≤ 5 × 1 0 3 1\le n\le k\le 5\times 10^3 1nk5×103

对于所有合法的 v i v_i vi,都有 1 ≤ v i ≤ 1 0 9 1\le v_i\le 10^9 1vi109

说明

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

题解

思路

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

代码

蒟蒻还不会 O ( n log ⁡ 2 n ) O\left ( n\log_{2}{n} \right ) O(nlog2n),只会 O ( n 2 ) O\left ( n^2 \right ) O(n2)

#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;
}
  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值