CSUSTOJ-小樱的库洛牌(恶心的构造题)
题目连接:http://acm.csust.edu.cn/problem/4020
CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/109541023
Description
众所周知,木之本樱有 \(n\) 张库洛牌,每张库洛牌按危险程度可以分成等级 \(1-n\) ,由于所有库洛牌都放在一起的太危险了,所以小樱想把 \(n\) 张库洛牌等分成 \(k\) 组,让每组危险程度的和的最大值和最小值的差值最小 ,但由于木之本樱只是友枝小学的学生,实在不知道该怎么分,于是她找到了聪明的 \(ACMer\) 。
input
第一行三个整数 \(n, k\) , \((1 \leq n \leq 1e5)\) , \((1 \leq k \leq n)\) ,并且保证 \(n\) 可以整除 \(k\)
output
输出 \(k\) 行,每行 \(\frac{n}{k}\) 个整数 (可以输出任意一组解)
Sample Input 1
4 2
Sample Output 1
1 4
2 3
这是个恶心的找规律和构造题目。。。我们设\(len=\frac{n}{k}\),即每组数的个数。很明显当\(len\%2==0\)的时候我们一定可以构造出\(1-n,2-(n-1)...\)这样两两相等的\(\frac{n}{2}\)个组合可以平均分配给每组。
接下来当\(len\)为奇数的时候才是重头戏,对于这种情况我们如果没有什么想法的话只能竟可能地去寻找规律。说实话我找了1个多小时才有意识地找到了QAQ。。。
我们可以发现的一个规律就是对于\(len\)为奇数而言,它分为2种情况,一个就是当\(k\)为偶数的时候,它的最小差值一定会是1,当\(k\)为奇数的时候他的最小差值是0。
那么我们现在就需要考虑怎么分配的问题了,很明显我们可以提出一个偶数的长度来减小工作量,那么我们肯定不能提出\(len-1\)这个偶数,因为这样的话就导致了最小差值为\(k-1\)了
那么我们可以考虑提出\(len-3\)这个偶数,那么前面的3个数就可以得到及时的调整,但我们目前所担心的就是前三个数的调整是否能够得到最优结果。事实上我们可以通过实验来验证的。我们将这个n个数分为\((1,2,3...,k),(1,2,3...,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)\)这样的构造,我们可以算出他们每组的值都是一样的,所以可以得到最小差值为0。那么按照上面的规律,我们按顺序一个一个往下走就完事了,第一列是往下走的,第二列是从中间开始往下走然后如果超出边界了则直接到1继续往下走也就相当于首位相接了,对于第三列则是从n往上走的,走的步长为2,也是相当于首位相接了。
接下来当这个\(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;
}