HDU 5355 Cake (WA后AC代码,详细解析,构造题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5355

题面:

Cake

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 1632    Accepted Submission(s): 273

Special Judge

Problem Description

There are m
soda and today is their birthday. The 1-st
soda has prepared n
cakes with size 1,2,…,n.
Now 1-st
soda wants to divide the cakes into m
parts so that the total size of each part is equal.

Note that you cannot divide a whole cake into small pieces that is each cake must be complete in them
parts. Each cake must belong to exact one of m
parts.

Input

There are multiple test cases. The first line of input contains an integerT,
indicating the number of test cases. For each test case:

The first contains two integers n
and m(1≤n≤105,2≤m≤10),
the number of cakes and the number of soda.

It is guaranteed that the total number of soda in the input doesn’t exceed 1000000. The number of test cases in the input doesn’t exceed 1000.

Output

For each test case, output "YES" (without the quotes) if it is possible, otherwise output "NO" in the first line.

If it is possible, then output m
lines denoting the m
parts. The first number si
of i-th
line is the number of cakes in i-th
part. Then si
numbers follow denoting the size of cakes in i-th
part. If there are multiple solutions, print any of them.

Sample Input

4
1 2
5 3
5 2
9 3

Sample Output

NO
YES
1 5
2 1 4
2 2 3
NO
YES
3 1 5 9
3 2 6 7
3 3 4 8

Source

2015 Multi-University Training Contest 6

解题:

比赛的时候以为是贪心,小数据试了几组发现,只要当前最大的数没取,并且小于我现在剩余的值,那么就取并标记,实际上是错的。比如取了13,纵然还能取12,却不一定要取12,因为取了12后,会造成无法凑成那个平均数,因此如此贪心是不对的,看赛后情况,估计绝大多数队伍都是水过的(貌似说当时Special Judge坏了),看是否正确跑下23 6这组数据大概就能知分晓了。

正确解法(题解方法):先预处理出40之内所有能划分的情况,然后后续的只需要往前面处理好的情况上凑就好了。

比如先处理好了6 3的情况,计算54 3时:

2 6 1

2 5 2

2 4 3

18 6 1 7 12 13 18 19 24 25 30 31 36 37 42 43 48 49 54

18 5 2 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53

18 4 3 9 10 15 16 21 22 27 28 33 34 39 40 45 46 51 52

红色部分是取的6 3处理好的结果,而后面蓝色部分则是两端对称构造的结果。

感觉题解讲的并不是很清晰,可能会有人误认为是一直用2m去减n,直至n小于40,那么便可以取前面已经预处理好的结果了,实际上还要满足(n-2m*x)要能被划分为y块。因此并不是从后往前减,而是从前往后搜寻第一个合法的状态。至于题解中40是如何产生的,渣渣实在是证明不了,但用了20去构造,发现是会不够用的,(测试中越界了)。

构造过程其实挺好理解的,就是高斯第1项和尾项的和等于第2项和倒数第2项的和,不断内移就行了。

总结:

有时候不要太过想当然,上次bc的三角形数也是,贪心不要乱用,得有一定依据。多校非常喜欢考察构造题,上次一张图,再上次24算,发现某些状态数比较大的时候,就应该联想到构造。

疑点:

题解中的40,不知道是怎么蹦出来的,求大神证明!!

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
#define mod 1000000007
using namespace std;
//访问和成功标记
bool status[45],sign;
//记录搜索路径
int path[45];
//平均值,cnt为当前第几组解
int ave,m,n,cnt;
//flag[x][y]表示x块蛋糕能不能划分为y部分
bool flag[45][12];
struct division
{
	int a[45],sz;
}store[45][12][12];
//三维分别表示共多少,划分为几块,它的第几块是,以及内部存储分别是哪几块
//预处理,搜索
//pos为当前位置,left为剩余容量,p为路径中第几个数字
void dfs(int pos,int left,int p)
{
	//当前状态下已经找到一种方案
	if(sign)return;
	//成功找到解
	if(left==0)
	{
		//把路径中的值存入到数组中
		for(int i=0;i<p;i++)
			store[n][m][cnt].a[i]=path[i];
		store[n][m][cnt].sz=p;
		//成功标记
		sign=true;
		return;
	}
	for(int i=pos;i>=1;i--)
	{
		if(!status[i]&&left>=i)
		{
			//该点已访问
			status[i]=1;
			path[p]=i;
			dfs(i,left-i,p+1);
			//避免把已经成功的点重新置为0
			if(sign)return;
			status[i]=0;
		}
	}
}
//预处理
void prep()
{
   memset(flag,0,sizeof(flag));
   int tmp,total;
   for(int i=1;i<=20;i++)
   {
	   //tmp是根据n要小于等于平均值,计算出来的上界
	   tmp=(i+1)/2;
	   total=i*(i+1)/2;
	   for(int j=1;j<=tmp;j++)
	   {
		   //如果可以划分的话
		   if(total%j==0)
		   {
			   flag[i][j]=1;
			   memset(status,0,sizeof(status));
			   ave=total/j;
			   m=j;
			   n=i;
			   //循环找多组解
			   for(int k=0;k<j;k++)
			   {
				   cnt=k;
				   sign=false;
				   dfs(n,ave,0);
			   }
		   }
	   }
   }
}
int main()
{
	//预处理
	prep();
	int t,x,y,a,b,sz;
	//读入
	scanf("%d",&t);
	LL total;
	while(t--)
	{
		scanf("%d%d",&x,&y);
        total=1LL*(1+x)*x/2;
		ave=total/y;
		//排除不能划分的情况
		if((total%y)||(ave<x))
		{
			printf("NO\n");
			continue;
		}
		//其余的都是可构造的
		printf("YES\n");
		m=y;
		//注意m已经乘以2
		m<<=1;
		int g;
		//找到第一个合法的状态
		for(g=1;;g++)
		{
			//如果可以划分,而且后面多出的数刚好可以构造
			if(flag[g][y]&&((x-g)%m)==0)
				break;
		}
		//b为构造次数
		b=(x-g)/m;
		for(int i=0;i<y;i++)
		{
			sz=store[g][y][i].sz;
			//总数量
			printf("%d",sz+(b<<1));
			//原有解
			for(int k=0;k<sz;k++)
				printf(" %d",store[g][y][i].a[k]);
			//构造解
			for(int k=0;k<b;k++)
				printf(" %d %d",k*m+g+i+1,(k+1)*m+g-i);
			printf("\n");
		}
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-18 03:21:55

HDU 5355 Cake (WA后AC代码,详细解析,构造题)的相关文章

HDU 5355 Cake (WA后AC代码,具体解析,构造题)

题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=5355 题面: Cake Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 1632    Accepted Submission(s): 273 Special Judge Problem Description There are s

HDU 5355 Cake

HDU 5355 Cake 更新后的代码: 今天又一次做这道题的时候想了非常多种思路 最后最终想出了自觉得完美的思路,结果却超时 真的是感觉自己没救了 最后加了记忆化搜索,AC了 好了先说下思路吧.不知道大家住没注意m<=10 我们能够把大部分的数据写成成对的形式比如n=27 m=6的这组数据 第1份  27  16 第2份  26  17 第3份  25  18 第4份  24  19 第5份  23  20 第6份  22  21 剩下1~15搜索出6等份分给全部人 这样成对出现的数蛇形数我

多校第六场 1003 hdu 5355 Cake(贪心)

题目链接:(数据加强后wa了) hdu 5355 题目大意: 给出一个蛋糕.切成1~n大小的n块.问是否能在不继续分割的情况下拼凑出m等份. 题目分析: 首先我们是可以知道每份蛋糕的尺寸的,利用n*(n+1)/2m可以算出来,假设不能整除的话,那么一定无解. 然后我们考虑怎样构造一组解,对于一块蛋糕,我想到了一个贪心策咯,尽量选择最大块的蛋糕,由于假设能选可是不选这块蛋糕,那么也一定是通过选取小块的蛋糕来拼凑出这块蛋糕的大小,可是假设小的拼凑出了这块,那么就不能用于拼凑其它的蛋糕,显然选择最大的

hdu 5355 Cake(构造+回溯)

题意: 给出一个蛋糕,切成1~n大小的n块,问能否在不继续切割的情况下拼凑出m等份. 解析: 首先可以求出这些蛋糕的总和n?(n+1)/2,如果总和sum%m != 0那么就不肯能被平分成m份,那么输出"NO". 接下来计算平均数avg=sum/m,如果平均数avg < n的话,蛋糕是不可能用完的,同样也输出"NO". 剩下的情况蛋糕是一定能拼成"YES"的,那么可以将这些蛋糕以2*m为单位一组一组的分配,每个人拿当前这组的最大和最小,次大

HDU 1059 Dividing 分配(AC代码)多重背包的变形

1 #include <iostream> 2 using namespace std; 3 int num[6]; 4 int dp[200]; 5 bool divide(int sum) 6 { 7 int k,i,j; 8 for(i=0;i<6;i++) 9 for(k=0;k<num[i];k++) 10 for(j=sum;j>i;j--) 11 if(dp[j-(i+1)]+(i+1)>dp[j]) 12 dp[j]=dp[j-(i+1)]+(i+1);

HDU 2191 Robberies抢劫案(AC代码)01背包的变形

1 #include <iostream> 2 #define limit 110 3 using namespace std; 4 int n; 5 int money[limit]; //银行的钱 6 double safe[limit]; //被抓的概率 7 double dp[10000]; 8 double p,big; 9 void cal(int temp,int n)//所有银行的钱,n家银行 10 { 11 int i,j; 12 for(i=0;i<n;i++) 13

HDU 5355 Cake(2015多校第六场,搜索 + 剪枝)

Cake Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 965    Accepted Submission(s): 119 Special Judge Problem Description There are m soda and today is their birthday. The 1-st soda has prepa

属性动画,代码详细解析

本文假定你已经对属性动画有了一定的了解,至少使用过属性动画.下面我们就从属性动画最简单的使用开始. ObjectAnimator .ofInt(target,propName,values[]) .setInterpolator(LinearInterpolator) .setEvaluator(IntEvaluator) .setDuration(500) .start(); 相信这段代码对你一定不陌生,代码中有几个地方是本文中将要重点关注的,setInterpolator(...).setE

HDU 2222 Keywords Search(AC自己主动机模板题)

题意:给出一个字符串和若干个模板,求出在文本串中出现的模板个数. 思路:由于有可能有反复的模板,trie树权值记录每一个模板出现的次数就可以. #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map&