CSUSTOJ-小樱的库洛牌(恶心的构造题)

题目连接:http://acm.csust.edu.cn/problem/4020
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13941902.html

Description

众所周知,木之本樱有 n n n 张库洛牌,每张库洛牌按危险程度可以分成等级 1 − n 1-n 1n ,由于所有库洛牌都放在一起的太危险了,所以小樱想把 n n n 张库洛牌等分成 k k k 组,让每组危险程度的和的最大值和最小值的差值最小 ,但由于木之本樱只是友枝小学的学生,实在不知道该怎么分,于是她找到了聪明的 A C M e r ACMer ACMer

input

第一行三个整数 n , k n, k n,k , ( 1 ≤ n ≤ 1 e 5 ) (1 \leq n \leq 1e5) (1n1e5) , ( 1 ≤ k ≤ n ) (1 \leq k \leq n) (1kn) ,并且保证 n n n 可以整除 k k k

output

输出 k k k 行,每行 n k \frac{n}{k} kn 个整数 (可以输出任意一组解)

Sample Input 1
4 2
Sample Output 1
1 4
2 3

这是个恶心的找规律和构造题目。。。我们设 l e n = n k len=\frac{n}{k} len=kn,即每组数的个数。很明显当 l e n % 2 = = 0 len\%2==0 len%2==0的时候我们一定可以构造出 1 − n , 2 − ( n − 1 ) . . . 1-n,2-(n-1)... 1n,2(n1)...这样两两相等的 n 2 \frac{n}{2} 2n个组合可以平均分配给每组。

接下来当 l e n len len为奇数的时候才是重头戏,对于这种情况我们如果没有什么想法的话只能竟可能地去寻找规律。说实话我找了1个多小时才有意识地找到了QAQ。。。

我们可以发现的一个规律就是对于 l e n len len为奇数而言,它分为2种情况,一个就是当 k k k为偶数的时候,它的最小差值一定会是1,当 k k k为奇数的时候他的最小差值是0。

那么我们现在就需要考虑怎么分配的问题了,很明显我们可以提出一个偶数的长度来减小工作量,那么我们肯定不能提出 l e n − 1 len-1 len1这个偶数,因为这样的话就导致了最小差值为 k − 1 k-1 k1

那么我们可以考虑提出 l e n − 3 len-3 len3这个偶数,那么前面的3个数就可以得到及时的调整,但我们目前所担心的就是前三个数的调整是否能够得到最优结果。事实上我们可以通过实验来验证的。我们将这个n个数分为 ( 1 , 2 , 3... , k ) , ( 1 , 2 , 3... , k ) . . . (1,2,3...,k),(1,2,3...,k)... (1,2,3...,k),(1,2,3...,k)...这样的 n / k n/k n/k组,然后我们就可以对于k组从这些组中每组挑选一个(这样的话由于每组有个基础值,所以我们可以都从1到k)这句话有点可能有点绕,但却是这题的最关键的地方。比如拿15 5举例,我们可以画出如下的匹配图形:
在这里插入图片描述
也就是得到了 ( 1 , 3 + 5 , 5 + 10 ) ( 2 , 4 + 5 , 3 + 10 ) ( 3 , 5 + 5 , 1 + 10 ) ( 4 , 1 + 5 , 4 + 10 ) ( 5 , 2 + 5 , 2 + 10 ) (1,3+5,5+10)(2,4+5,3+10)(3,5+5,1+10)(4,1+5,4+10)(5,2+5,2+10) (1,3+5,5+10)(2,4+5,3+10)(3,5+5,1+10)(4,1+5,4+10)(5,2+5,2+10)这样的构造,我们可以算出他们每组的值都是一样的,所以可以得到最小差值为0。那么按照上面的规律,我们按顺序一个一个往下走就完事了,第一列是往下走的,第二列是从中间开始往下走然后如果超出边界了则直接到1继续往下走也就相当于首位相接了,对于第三列则是从n往上走的,走的步长为2,也是相当于首位相接了。

接下来当这个 k k k为偶数的时候他就会发生变化,如下图:
在这里插入图片描述
第三列的2往上跳2步的时候到达了6这个位置,但6已经被占据了,只能退而求其次连上5这个数,那么这也就导致了这个差值为1的出现。接下来后面的所有数都会比正常的少1。

于是规律推完,此题结束!

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mac=1e5+10;

vector<int>g[mac];
int vis[mac];

int main(int argc, char const *argv[])
{
	int n,k,len;
	scanf ("%d%d",&n,&k);
	if (k==1){
		for (int i=1; i<=n; i++)
			printf("%d%c",i,i==n?'\n':' ');
		return 0;
	}
	len=n/k;
	if (len%2==0){
		int nb=1;
		for (int i=1; i<=len/2; i++){
			for (int j=1; j<=k; j++){
				g[j].push_back(nb);
				g[j].push_back(n-nb+1);
				nb++;
			}
		}
		for (int i=1; i<=k; i++){
			for (auto x:g[i])
				printf("%d ",x);
			printf("\n");
		}
	}
	else {
		int ns=k;
		vis[ns]=1;
		for (int i=1; i<=k; i++){
			g[i].push_back(i);
			if (len>1){
				int su=(i+k/2)%(k+1);
				if (i+k/2>k) su++;
				g[i].push_back(su);
				g[i].push_back(ns);
				ns-=2;
				if (ns<=0) ns+=k;
				if (!vis[ns]) vis[ns]=1;
				else vis[--ns]=1;
			}
		}
		if (len>3){
			for (int i=1; i<=k; i++){
				for (int j=4; j<=(len-3)/2+3; j++){
					g[i].push_back(i);
					g[i].push_back(k-i+1);
				}
			}
		}
		for (int i=1; i<=k; i++){
			int sum=0;
			for (int j=0; j<len; j++){
				printf("%d ",g[i][j]+sum);
				sum+=k;
			}
			printf("\n");
		}
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值