题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5355
题意:给你n个尺寸大小分别为1,2,3,…,n的蛋糕,要求你分成m份,要求每份中所有蛋糕的大小之和均相同,如果有解,输出“YES”,并给出每份的蛋糕数及其尺寸大小,否则输出“NO”
例如n=5,m=3,即大小尺寸分别为1,2,3,4,5的5个蛋糕,要求分成三份,那么解可以是第一份一个蛋糕,大小为5;第二份两个蛋糕,大小为1、4;第三份两个蛋糕,大小为2、3。这样每份大小之和均为5,满足题目要求。
解法:首先得放一下这个题解:http://blog.csdn.net/queuelovestack/article/details/47321211 写的很好,这个问题的关键点在于判断出有合法方案时,我们可以将这些蛋糕按照2*m为单位一组一组的分配,每个人拿当前这组的最大最小,次大次小。。。
然后做完这个过程直到剩余[0,4*m],这个看代码就知道了。对于这个区间的值就直接爆搜即可。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5+10; typedef long long LL; vector <int> ans[15]; LL sumv[15], cake[maxn]; bool vis[maxn]; LL n, m, dis, res; //[0-4*m]的DFS bool dfs(int cur, int sum, int pos) { if(cur == m+1) return true; for(int i=res; i>=pos; i--){ if(vis[i]) continue; if(sum+i==dis){ cake[i]=cur; vis[i]=1; if(dfs(cur+1,0,1)) return true; vis[i]=0; return false; } else if(sum+i<dis){ cake[i]=cur; vis[i]=1; if(dfs(cur,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); memset(sumv, 0, sizeof(sumv)); memset(vis, false, sizeof(vis)); memset(cake, 0, sizeof(cake)); for(int i=0; i<=m; i++){ ans[i].clear(); } LL sum = n*(n+1)/2; if(sum%m!=0){ puts("NO"); continue; } LL ave = sum/m; if(ave < n){ puts("NO"); continue; } puts("YES"); res = n%(2*m); //23%(12)=11 if(res!=0){ res += 2*m; //res=11+12=23 res = min(res, n); } // //23 6 int a,b; for(int i=n; i>res; i-=(2*m)){ for(int k=1; k<=m; k++){ a=i-k+1,b=i-(2*m)+k;// //23 12 //22 13 //... ans[k].push_back(a); ans[k].push_back(b); sumv[k]+=a, sumv[k]+=b; } } dis = ave - sumv[1]; dfs(1, 0, 1); for(int i=1; i<=res; i++){ ans[cake[i]].push_back(i); } for(int i=1; i<=m; i++){ int sz = ans[i].size(); printf("%d", sz); for(int j=0; j<sz; j++){ printf(" %d", ans[i][j]); } puts(""); } } return 0; }