原题链接:
洛谷https://www.luogu.com.cn/problem/P1120
信息奥赛一本通http://ybt.ssoier.cn:8088/problem_show.php?pid=1442

这个题目是一个搜索题,主体思路就是从小到大枚举每一个长度然后验证是否满足条件,但直接暴力搜索会超时,需要对其进行优化。
https://www.luogu.com.cn/record/30758332

(这是暴力搜索的代码,只得了18分)
#include<stdio.h> int a[51],b[51]; int n,s,ye=0,ans=0,cs; void ss(int a1,int a2){ if(ye)return; int k=1; for(int i=1;i<=50;i++)if(a[i]){k=0;} if(k!=0&&a2==0){ans=1;ye=1;} if(a2==0)ss(a1+1,cs); for(int i=1;i<=a2;i++)if(a[i]){ a[i]--; ss(a1,a2-i); a[i]++;} return;} int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&s); if(s>50)continue; b[s]++;a[s]++; } for(int i=1;i<=3250;i++){ ye=0;ans=0;cs=i; ss(0,cs); if(ans){printf("%d",i);return 0;} } return 0;}
经过本人的反复摸索(并且借鉴了dalao的解法),终于找到了几个优化办法:
1.搜索时从大到小枚举;
2.若拼接当前木棍时已用了一根长为X的木棍,则下一次搜索时从长度X开始搜索,尽量减少重复搜索;
3.枚举的长度应该是所有木棍总长度的因数;
4.若某组拼接不成立,且此时已拼接的长度为0或当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环,因为此时搜索结果是不成立,那么继续搜索得到的也不成立;
5.用桶来存储可能更好,因为范围是1-50。
经过优化,得到了100分。
https://www.luogu.com.cn/record/30758337
最后附上AC代码。
C:
#include<stdio.h> int a[51]; int n,s,ye=0,ans=0,cs,maxx=0,zong=0; int minn(int a1,int a2){ return a1<a2?a1:a2;} void ss(int a1,int a2){ if(ye)return; int k=1; for(int i=1;i<=maxx;i++)if(a[i]>0){k=0;break;} if(k!=0&&a2==0){ans=1;ye=1;} if(a2==0)ss(maxx,cs); for(int i=minn(a1,a2);i>0;i--)if(a[i]>0){ a[i]--; ss(i,a2-i); a[i]++; if(a2==cs||a2==i)break; } return;} int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&s); if(s>50)continue; a[s]++; zong+=s; if(s>maxx)maxx=s; } for(int i=maxx;i<=zong;i++)if(zong%i==0){ ye=0;ans=0;cs=i; ss(maxx,cs); if(ans){printf("%d",i);return 0;} } return 0;}
Pascal:
var a:array[0..51]of longint; n,s,cs,maxx,zong:longint; ye,ans:boolean; function minn(a1,a2:longint):longint; begin if a1<a2 then exit(a1) else exit(a2); end; procedure ss(a1,a2:longint); var k:boolean; i:longint; begin if ye then exit(); k:=true; for i:=1 to maxx do begin if a[i]>0 then begin k:=false; break; end; end; if(k=true) and (a2=0) then begin ans:=true; ye:=true; exit(); end; if a2=0 then ss(maxx,cs); for i:=minn(a1,a2) downto 1 do begin if a[i]>0 then begin a[i]:=a[i]-1; ss(i,a2-i); a[i]:=a[i]+1; if(a2=cs) or (a2=i) then break; end; end; exit(); end; var j:longint; begin ye:=false; ans:=false; maxx:=0; zong:=0; readln(n); for j:=1 to n do begin read(s); if s>50 then continue; a[s]:=a[s]+1; zong:=zong+s; if s>maxx then maxx:=s; end; for j:=maxx to zong do begin if (zong mod j)=0 then begin ye:=false; ans:=false; cs:=j; ss(maxx,cs); if ans then begin writeln(j); break; end; end; end; end.
如果有什么问题,请指出,谢谢。