MYF

HDU 5355 Cake

题目链接

HDU 5355

解题方法:用DFS+回溯构造前40组,然后再模拟的加到这40组上

题目分析

题目大意

给定n个蛋糕,大小分别为1~n,需要把这n个蛋糕平均分给m个人,也就是说,每个人可以得到任意块蛋糕,但是每个人得到的蛋糕总大小相同,问如何平分,如果可以输出YES,并输出m行,每一行先输出这个人获得的块数k,然后跟随k个数,表示哪k块蛋糕。

解析

挺恶心的一道题,参考题解:HDU 5355 Cake (WA后AC代码,详细解析,构造题)

确定n, m 这个组合能否满足条件有两个判定:

  • 每个人获得的平均值应当小于等于n,即需要满足n*(n+1)/2m≥n
  • 因为蛋糕不能切,所以需要满足total % m == 0, total = n*(n+1)/2

先构造n <= 40的所有情况。用DFS去跑每一个平均值,每次跑,并进行回溯,直到跑到剩余数为0return,跑出来m次就把这m组解存储到v[n][m][i][]中,其中i表示这m次中的第i次。

对于n <= 40的数据,可以直接从v中读取答案,对于n > 40的数据,则需要进行进一步构造。如n=54, m=3时,可以通过n=6, m=3构造形成,当1~6确定下来之后,只需要确定7~54即可,共计48个数字,可以首位和为61的24组(k组),如7 54,8 55,9 56…然后把这24组再分为m组,匹配给这m个人。

由此可以看出,对于任意一组n, m,需要先找出n‘ = n - k * (2m),从表中先找到n', m的解,然后将这k组数分配到m个人头上即可。

最后,跟各位大牛表示同不明白的一个点是:为什么要构建40组?

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Memset(a,val) memset(a,val,sizeof(a))
#define PI acos(-1)
#define rt(n) (i == n ? '\n' : ' ')
#define hi printf("Hi----------\n")
#define IN freopen("input.txt","r",stdin);
#define OUT freopen("output.txt","w",stdout);
#define debug(x) cout<<"Debug : ---"<<x<<"---"<<endl;
#pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long ll;
using namespace std;
const int maxn=100000+5;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
vector<int>v[45][12][12];
bool cando[45][12];//cando[n][m]表示n,m组合能否成功
int status[45];
int n,m,path[45];
bool ok;
void dfs(int cnt,int pos,int left,int step){
if (ok)
return;
//第cnt个人已经分到了ave块
if (left==0) {
for (int i=1; i<=step; i++) {
v[n][m][cnt].push_back(path[i]);
}
ok=true;
return;
}
for (int i=pos; i>=1; i--) {
if (!status[i]&&left>=i) {
status[i]=1;
path[++step]=i;
dfs(cnt, i-1, left-i, step);
if (ok)
return;
status[i]=0;
}
}
}
void prep(){
int ub,total,ave;
Memset(cando, false);
for (int i=1; i<=40; i++) {//此处为什么是40,并不理解
ub=(i+1)/2;//由(n+1)*n/(2*m)>=n推导得到, ub为可取到的m的最大值
total=i*(i+1)/2;
for (int j=1; j<=ub; j++) {
if (total%j==0) {
cando[i][j]=true;
ave=total/j;
n=i,m=j;
memset(status, 0, sizeof(status));
//找到k组解,每一组的和均为ave
for (int k=1; k<=m; k++) {
ok=false;
dfs(k,n,ave,0);
}
}
}
}
return;
}
int main(){
prep();
int t;
scanf("%d",&t);
while (t--) {
scanf("%d%d",&n,&m);
ll nn=(ll)n,mm=(ll)m;
ll total=(1+nn)*nn/2;
ll ave=total/mm;
if (total%mm||ave<nn) {
puts("NO");
continue;
}
puts("YES");
int dd=0,tt;//基础解
while (!cando[++dd][m]||(n-dd)%(2*m));
tt=(n-dd)/2;//构造次数

int ll=dd+1,rr=n;
for (int i=1; i<=m; i++) {
int sz=v[dd][m][i].size();
printf("%d",tt/m*2+sz);

// 找基础解
for (int kk=0; kk<sz; kk++)
printf(" %d",v[dd][m][i][kk]);

// 找构造解
for (int j=0; j<tt/m; j++,ll++,rr--)
printf(" %d %d",ll,rr);
puts("");
}
}
}