题目连接:http://acm.csust.edu.cn/problem/4020
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13941902.html
Description
众所周知,木之本樱有 n n n 张库洛牌,每张库洛牌按危险程度可以分成等级 1 − n 1-n 1−n ,由于所有库洛牌都放在一起的太危险了,所以小樱想把 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) (1≤n≤1e5) , ( 1 ≤ k ≤ n ) (1 \leq k \leq n) (1≤k≤n) ,并且保证 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)... 1−n,2−(n−1)...这样两两相等的 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 len−1这个偶数,因为这样的话就导致了最小差值为 k − 1 k-1 k−1了
那么我们可以考虑提出
l
e
n
−
3
len-3
len−3这个偶数,那么前面的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;
}