题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5355
解题思路:
官方题解:
Summary
You are given n integers 1, 2, . . . , n and an integer m. You need to divide the n integer into mequal sum parts (or determine it is impossible).
Solution
- If m is not divisible by 2n(n+1) or n<2m−1, it is impossible.
- If we have 2m number 1, 2, . . . , 2m, we can divide it into m equal sum parts easily.
- If n is large, we can reduce n to n−2m. If n(n≤40) is small, we can use backtracking to find a possible solution or we can construct the solution by hand.
算法思想:
首先可以求出这些蛋糕的总和n*(n+1)/2,如果总和sum%m != 0那么就不肯能被平分成m份,那么输出”NO”。
接下来计算平均数ave=sum/m,如果平均数ave < n的话,蛋糕是不可能用完的,同样也输出”NO”。
剩下的情况蛋糕是一定能拼成”YES”的,那么可以将这些蛋糕以2*m为单位一组一组的分配,每个人拿当前这组的最大和最小,次大和次小。。。直至拿到剩下[0,4*m]个蛋糕之间停止。
这时候进行暴力求解,问题变成了剩下的数,怎样放置在每行才能使和相同,这里直接暴力搜索dfs就行了。。。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
ll n,m,dis,ans;
vector<int> v[55];
int vis[110],pos[110],num[55];
bool dfs(int cnt,int sum,int id){// ans个数分成和相等的m份(id为第cnt份中最小的数,爆搜)
if(cnt == m+1)
return true;
for(int i = ans; i >= id; i--){
if(vis[i])
continue;
if(sum+i == dis){
pos[i] = cnt;
vis[i] = 1;
if(dfs(cnt+1,0,1))
return true;
vis[i] = 0;
return false;
}
else if(sum+i < dis){
pos[i] = cnt;
vis[i] = 1;
if(dfs(cnt,sum+i,i+1))
return true;
vis[i] = 0;
}
}
return false;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&m);
ll sum = n*(n+1)/2;
if(sum % m){
printf("NO\n");
continue;
}
ll ave = sum/m;
if(ave < n){
printf("NO\n");
continue;
}
printf("YES\n");
ans = n %(2*m);
if(ans != 0){
ans += 2*m;//最后一组有ans+2*m个数
ans = min(ans,n);//防止ans比n还大 例如:3 2这组数据
}
for(int i = 1; i <= m; i++)
v[i].clear();
memset(num,0,sizeof(num));
for(int i = n; i > ans; i -= (2*m)){//分成2*m份
for(int j = 1; j <= m; j++){
int x = i-j+1;
int y = i-2*m+j;
v[j].push_back(x);
v[j].push_back(y);
num[j] += (x+y);//第j行目前的和
}
}
dis = ave - num[1];//每行还差dis达到的ave(每行的目标和)
memset(vis,0,sizeof(vis));
memset(pos,0,sizeof(pos));
dfs(1,0,1);//1到ans的数,怎样放置在每行才能和相同(和==dis)
for(int i = 1; i <= ans; i++)
v[pos[i]].push_back(i);
for(int i = 1; i <= m; i++){
int l = v[i].size();
printf("%d",l);
for(int j = 0; j < l; j++){
printf(" %d",v[i][j]);
}
printf("\n");
}
}
return 0;
}