dividing zoj 1149, PKU 1014

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=149

这道题目真是打击人的自信心啊,在题目分类中被分为dp(动态规划),但是又不是随便dp的出来的。Google了好久,发现一般有以下几种解法:

  • 基本的动态规划
  • 双向dp
  • 居然还有人直接迭代搞定的。他的代码中优先考虑了sum/2为奇数而1,3,5的数量同时为偶数的情况,然后迭代。我没有验证,当然也没必要验证,因为这显然很投机。就像完全不懂的人买了一只股票,然后涨了,第二天还在那里沾沾自喜,表明自己是多么的英明。

对于上面所列出的方法,分析他们的代码,发现都不是很完美。虽然可以AC,但是看它们的时间基本是O(n2)的,但是题目的规模为20k,那么大概有4亿次计算,显然一秒钟是不够的。因此,这些方法基本上就是可以AC,但是心里总是不爽的。

那么我们来看一个比较爽的解决方案(此方案基本抄袭于http://www.blogjava.net/zellux/archive/2007/07/30/133416.html)。如果你觉得我的文字就像新宇体一样令人费解的话,那推荐看链接。

此方案基本基于一个逻辑的推理,此推理的结论为:对于任意一种珠宝的个数n,如果n>=13, 可以将n改写为 11(n为奇数) 或 12(n为偶数)。下面是推理过程:

首先以价值为6的珠宝为例,假设数量为n(>=13)。考虑两种情况:

  • 可以平分为两堆。那么再分两种情况。
    • 平分的两堆中都有6的宝珠,那么显然在两堆都去掉一个6依然是平均的。所以n-2的情况也可平分。
    • 只有一堆有6,那么这一堆至少有13颗价值为6的珠宝。考虑没有6的一堆。
      • 对于价值为5的珠宝,如果至少有6颗,那么可以将6颗5和另一堆的5颗6交换,造成两堆都包含6的情况,此种情况已证。因此5的数量必须小于6,那么最多为5颗。
      • 同样方式考察4,3,2,1,可以发现他们的最大数量为2,1,2,5
      • 由此可得,没有6的这一堆居然最多只能有5*5 + 4*2 + 3*1 + 2*2 + 1*5 = 25 + 8 + 3 + 4 + 5 = 45。而显然另外一堆最多有13×6 >45,那么和可以平分的前提矛盾。此种假设失败。
  • 不能平分为两堆,显然减少偶数个6都不能使得珠宝可以平分(可以反证法)。

由此可见,推理结论对于6是成立的,诸位可以找张纸比划下其他的数值。最后可以发现推理是成立的,当然我就不再浪费我宝贵的睡觉时间做重复劳动了。

利用此推理,我们可以将系统传给我们的宝珠数量减少到11/12,那么这个时候再使用dp或者迭代/递归,都能轻松解决。即使是最白痴的暴力法,要做到TLE也是很有难度的。

最后贴上我AC的代码(PS:我讨厌伸手党):

#include<stdio.h> #include<iostream> using namespace std; int marbles[7]; long half; bool fun(int a, long current_sum) { if(a>6) return false; for(int i=0;i<=marbles[a];i++) { if(current_sum== half) { return true; } if(fun(a+1, current_sum)) return true; current_sum+=a; } return false; } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "rt", stdin); freopen("output.txt", "wt", stdout); #endif int loop = 0; while(++loop) { long sum = 0; for(int i=1;i<=6;i++) { cin >> marbles[i]; if(marbles[i] > 12) { marbles[i] = 12-marbles[i]%2; } sum += i*marbles[i]; } if(sum == 0) { break; } if(sum%2==1) { cout << "Collection #" << loop <<":/nCan't be divided./n/n"; continue; } half = sum/2; if(fun(1, 0)) { cout << "Collection #" << loop <<":/nCan be divided./n/n"; } else { cout << "Collection #" << loop <<":/nCan't be divided./n/n"; } } }  

你可能感兴趣的:(Google,n2,fun)