毒瘤良心管理员非常毒瘤良心地对本题某些数据的时间限制进行修改,深入研究后发现其实可以将所有数据的时限改成200ms(逃。
本题有非常多的剪枝技巧
1.可以在定义变量maxn,并在输入的时候寻找最长♂的木棍长度,然后定义变量ans,来计算所有小木棍总和长度。我们可以只枚举maxn到ans/2间木棍长度,大大减少枚举数量。(可以只枚举一半是因为先dfs长度,如果找不到一定是原来只有一根。(很显然)(逃
2.在dfs之前先判断该数是否ans|i,如果是再dfs,减少了枚举数量。
事实上只用这两个剪枝方法就可以a掉这题,不过前文提到可以通过剪枝使最大数据可以在200ms之内通过,接下来先贴上运用前两种方法的代码然后再剪枝到毒瘤时间
#include<bits/stdc++.h> using namespace std; int n,tot=0,ans=0,maxn=0,minn=0x7fffffff; int num[3600]={0}; void dfs(int last,int now,int need,int can){ //四个变量分别表示 还需要几根木棍 if(last==0){printf("%d",need); exit(0);} // 已经凑出木棍的长度 if(now==need){dfs(last-1,0,need,maxn); return;} // 需要木棍长度 for(int i=can;i>=minn;i--){ //目前可用最大木棍长 if((num[i]!=0&&(i+now<=need))){ //简单的dfs过程 num[i]--; dfs(last,now+i,need,i); num[i]++; if(now==0||now+i==need) return; } } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int a; scanf("%d",&a); if(a<=50){ num[a]++; ans+=a; maxn=max(a,maxn); // 在输入过程中处理出最大的木棍长度和最小的 minn=min(a,minn); //木棍长度,完成剪枝 } } for(int i=maxn;i<=ans/2;i++) if(ans%i==0) dfs(ans/i,0,i,maxn); printf("%d",ans); return 0; }
接下来是将最大数据剪枝到200ms的玄学剪枝
3.根据某表情符大佬的理论,一根比较长的棍子在本题是不如一根比较短的棍子的,直观的感受一下,确实如此,你长归你长,但是你的灵活运用上不如短的棍子,所以我们用一遍sort,使得较长的棍子排到前面去。
4.用过的棍子长度相同就不要再用一次~~(桶装数据表示其实上面两个优化都有)~~
5.非常难想到的一点::如果当前长棍剩余的未拼长度等于当前木棍的长度或原始长度,继续拼下去时却失败了,就直接回溯并改之前拼的木棍。
解释起来就是,你和要的一样长,但是你却不能自组,你都不能自组,难为那些比你还短的木棍干嘛呢
以上就是P1120 【数据加强版】~~(臭不要脸明明降低时限)~~
个人认为如果只是a掉这题可能只有【普及+/提高】绿题水准,但是如果要求全部点都在200ms以内的话,可能有【提高+/省选-】,建议管理员再加强数据(逃
最后感谢 ______ 大佬
还有某陈巨神