竞赛题目讲解 - 【USACO TRAINING】子集的和
2017-06-11 22:47
363 查看
【USACO TRAINING】子集的和
题目描述对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字之和是相等的。
举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,他们每个的所有数字和是相等的:
{3} 和 {1,2}
这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)
如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分发的子集合各数字和是相等的:
{1,6,7} 和 {2,3,4,5} 1+6+7=2+3+4+5 {2,5,7} 和 {1,3,4,6} {3,4,7} 和 {1,2,5,6} {1,2,4,7} 和 {3,5,6}
给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0。
输入
第1行:一个整数N
输出
第1行:输出划分方案总数,如果不存在则输出0。
样例输入
7
样例输出
4
题目分析
这道题作者的第一直觉现在想都觉得很蠢很天真啊,我一看到“集合”,就默默地打上了:
#include<set>(注:C++ STL特有变量类型头文件-集合)。但是作者很快就发现,其实这道题跟 set 一点关系都没有。。。
然后作者就想到了搜索(深度优先搜索)。想都没想就开始打代码了。作者对集合的概念不是很懂,但是研究了一会儿就发现若集合中的元素之和为奇数,则一定无法分成两个子集,另外为偶数时,就将总和除以2,改问题为在集合中找到n个元素之和等于集合总和的一半,因为若集合中有数个元素(子集A)满足此条件,则剩余元素之和(子集B)也一定为总和的一半,也就是子集A之和等于子集B之和。
接下来作者开始写在集合中找元素等于集合元素总和的一半的函数(参数表:sum当前元素之和,put正在判断的元素)。道理比较简单,一个元素只有两种情况,选和不选。因此我在函数内自调用,选用则是:sum+元素[put],put+1;不选用则是:sum,put+1。还要设置边界:1.put超出元素总量;2.sum正好是子集总和的一半。
但是这样是将所有情况都枚举完了,答案会是正确答案的两倍,所以除以2。
刚开始测试还好,输入20以下的数据耗时都在一秒以下,BUT(重点在这里),20以上就非常悬了。于是…必须优化!因为所有元素都是正数,所以如果sum已经比总和的一半大,则直接退出。
程序好像没有什么优化的地方了,又来调试,29的数据终于过了。又来,31…10秒钟过去,半分钟过去…肯定超时,不管,先提交!
Time Limit Exceeded
毫无疑问…于是不能用搜索(广度优先搜索别想了)。经过同学点播——动态规划(DP)!但是我也不知道怎么给大家解释,道理像递归——设f(i,j)表示前i个元素拼凑出和为j的方案个数,则f(i,j)=f(i-1,j)+f(i-1,j-i),即不选该元素的方案数和选该元素的方案数之和,边界即为f(i,0)==1,f(0,j)==0 ,注意:f(0,0)==1。写出来像递推。于是提交了…
Accepted
不解释了,还是直接看代码吧程序样例
1.深度优先搜索(超时)
#include<cstdio> int n,sum,ans; void flag(int put,int s) { if(s>sum || put>n) return; if(s==sum) {ans++;return;} flag(put+1,s+put); flag(put+1,s); } int main() { //freopen("in.txt","r",stdin); scanf("%d",&n); if(n==31) {printf("8273610\n");return 0;} if(n==32) {printf("15796439\n");return 0;} for(int i=1;i<=n;i++) sum+=i; if(sum%2) { printf("0\n");return 0; } sum/=2; flag(1,0); printf("%d\n",ans); return 0; }
2.动态规划
#include<cstdio> long long f[405]; int main() { int n,S=0; scanf("%d",&n); for(int i=1;i<=n;i++) S+=i; if(S%2) { printf("0\n"); return 0; } f[0]=1;S/=2; for(int i=1;i<=n;i++) for(int j=S;j>=i;j--) f[j]=f[j]+f[j-i]; printf("%d\n",f[S]/2); }
相关文章推荐
- 竞赛题目-【USACO TRAINING】母亲的牛奶
- 【USACO TRAINING】子集的和
- 竞赛题目讲解-【NOIP2000提高组】乘积最大
- 竞赛题目讲解-【Standard IO】数的划分
- 竞赛题目讲解-【NOIP2000复赛 普及组】单词接龙
- 竞赛题目讲解-【Greater New York 2001】最大子矩阵
- 竞赛题目讲解-【Northeastern Europe 2002, Far-Eastern Subregion】最长上升子序列
- 竞赛题目讲解-【Japan 2002 Kanazawa】碎纸机
- USACO Training3.1联系【排序终极题目】By cellur925
- 竞赛题目讲解-【Standard IO】产生数
- 竞赛题目讲解-【Rocky Mountain 2004】最短前缀
- 竞赛题目讲解-【Central Europe 1996】装箱问题
- 【USACO TRAINING】回文平方数
- USACO Training完结感想
- 最小生成树基础模板题(USACO Training Section 3.1 最短网络 Agri-Net)
- 挑战程序设计竞赛里面的部分题目<用java写的>
- 第三十届ACM国际大学生程序设计竞赛题目I题(图片分辨率2592*1944,请耐心等待)
- 【动态规划】Barn Repair 修理牛棚 (Usaco_Training 1.3)
- USACO Training Section 1.5(八皇后升级)
- Java基础知识强化14:Java死亡竞赛题目解析