[jzoj]1501. 糖果(优化多维背包的多种方法)
2017-05-31 14:24
267 查看
一般来说,求解多维背包是求最小价值,但是也可以求其他的,例如此题:
多维背包复杂度O(nmk),但有很多的优化方法.
100%的数据1<=N<=100,1<=Ki<=500,1<=Ci<=200.
对于此题,可以拿到72分.
可以多过一个数据点,并且对于刚刚跑400ms的一个数据,现在只跑了40ms,可见优化之大,但由于数据的原因,并不能多拿多少分.
于是继续优化.
于是这样子,可以拿到92分,进了一大步.
我们把Ai∗Di,k按从小到大排个序,可以发现刚刚跑900ms+的点只跑了500ms+,优化了近0.4秒.
当然,还有一个更加强劲的优化,我们判断如果当前可行的最优解已经产生,那么就没必要继续进行dp了,直接输出.
注意常数,如果把判断放在第三重循环里,就会多将近0.1秒.
即枚举一个状态,并通过这个状态由一个背包分成两份去更新下一个状态:
这种方法对于这道题的数据很吃香,最大的一个点只跑了20ms,考试时可以将以上两种方法并用,使得正确率最高!
多维背包复杂度O(nmk),但有很多的优化方法.
Link
https://jzoj.net/senior/#contest/show/1992/2Problem
给定n个背包的重量ci及个数ki,使其分成两堆使得差最小.Data constraint
50%的数据1<=N,Ki,Ci<=100.100%的数据1<=N<=100,1<=Ki<=500,1<=Ci<=200.
Solution
Method-1-1
我们直接采用多维背包求解,不加任何优化:var f:array[0..10000000] of boolean; x,y:array[1..100] of longint; n,i,j,k,sum,ans:longint; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; begin readln(n); for i:=1 to n do begin readln(x[i],y[i]); inc(sum,x[i]*y[i]); end; f[0]:=true; for i:=1 to n do for k:=1 to x[i] do for j:=sum downto y[i] do if f[j-y[i]] then f[j]:=true; ans:=sum; for i:=1 to sum do if f[i] then ans:=min(ans,abs(i-(sum-i))); writeln(ans); end.
对于此题,可以拿到72分.
Method-1-2
我们采取两个优化:二进制优化和每次sum依次累加的优化.var f:array[0..10000000] of boolean; d:array[1..100,1..100] of longint; x,y,len:array[1..100] of longint; n,i,j,k,t,sum,ans:longint; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; begin readln(n); for i:=1 to n do begin readln(x[i],y[i]); t:=x[i]; k:=1; while t-k>0 do begin t:=t-k; inc(len[i]); d[i,len[i]]:=k; k:=k shl 1; end; inc(len[i]); d[i,len[i]]:=t; end; f[0]:=true; for i:=1 to n do begin inc(sum,x[i]*y[i]); for k:=1 to len[i] do for j:=sum downto y[i]*d[i,k] do if f[j-y[i]*d[i,k]] then f[j]:=true; end; ans:=sum; for i:=1 to sum do if f[i] then ans:=min(ans,abs(i-(sum-i))); writeln(ans); end.
可以多过一个数据点,并且对于刚刚跑400ms的一个数据,现在只跑了40ms,可见优化之大,但由于数据的原因,并不能多拿多少分.
于是继续优化.
Method-1-3
注意到1<=Ci<=200,所以我们可以考虑把相同重量的背包合并在一起,那么二进制的优化就会明显很多.var tot,a,len:array[0..200] of longint; bz:array[0..200] of boolean; f:array[0..10000000] of boolean; d:array[0..100,1..100] of longint; n,i,j,k,t,sum,ans,lena,x,y,maxsum:longint; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; begin readln(n); fillchar(bz,sizeof(bz),true); for i:=1 to n do begin readln(x,y); inc(maxsum,x*y); inc(tot[y],x); if bz[y] then begin bz[y]:=false; inc(lena); a[lena]:=y; end; end; for i:=1 to lena do begin t:=tot[a[i]]; k:=1; while t-k>0 do begin t:=t-k; inc(len[i]); d[i,len[i]]:=k; k:=k shl 1; end; inc(len[i]); d[i,len[i]]:=t; end; f[0]:=true; for i:=1 to lena do begin inc(sum,a[i]*tot[a[i]]); for k:=1 to len[i] do for j:=sum downto a[i]*d[i,k] do if f[j-a[i]*d[i,k]] then f[j]:=true; end; ans:=sum; for i:=1 to sum do if f[i] then ans:=min(ans,abs(i-(sum-i))); writeln(ans); end.
于是这样子,可以拿到92分,进了一大步.
Method-1-4
我们再来两个优化:我们把Ai∗Di,k按从小到大排个序,可以发现刚刚跑900ms+的点只跑了500ms+,优化了近0.4秒.
当然,还有一个更加强劲的优化,我们判断如果当前可行的最优解已经产生,那么就没必要继续进行dp了,直接输出.
注意常数,如果把判断放在第三重循环里,就会多将近0.1秒.
var tot,a,len:array[0..200] of longint; bz:array[0..200] of boolean; f:array[0..10000000] of boolean; d:array[0..100,1..100] of longint; n,i,j,k,t,sum,ans,lena,x,y,maxsum:longint; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; begin readln(n); fillchar(bz,sizeof(bz),true); for i:=1 to n do begin readln(x,y); inc(maxsum,x*y); inc(tot[y],x); if bz[y] then begin bz[y]:=false; inc(lena); a[lena]:=y; end; end; for i:=1 to lena do begin t:=tot[a[i]]; k:=1; while t-k>0 do begin t:=t-k; inc(len[i]); d[i,len[i]]:=k; k:=k shl 1; end; inc(len[i]); d[i,len[i]]:=t; end; for i:=1 to lena-1 do for j:=i+1 to lena do if a[i]*tot[a[i]]>a[j]*tot[a[j]] then begin a[0]:=a[i]; a[i]:=a[j]; a[j]:=a[0]; len[0]:=len[i]; len[i]:=len[j]; len[j]:=len[0]; d[0]:=d[i]; d[i]:=d[j]; d[j]:=d[0]; end; f[0]:=true; for i:=1 to lena do begin inc(sum,a[i]*tot[a[i]]); for k:=1 to len[i] do begin for j:=sum downto a[i]*d[i,k] do if f[j-a[i]*d[i,k]] then f[j]:=true; if f[maxsum div 2] then begin if maxsum mod 2=1 then writeln(1) else writeln(0); halt; end; end; end; ans:=sum; for i:=1 to sum do if f[i] then ans:=min(ans,abs(i-(sum-i))); writeln(ans); end.
Method-2
根据这种题的一般规律,答案差是不会很大的, 我们限定在一个最大却又时间稳妥的范围,然后直接按照一般思路dp就好了.即枚举一个状态,并通过这个状态由一个背包分成两份去更新下一个状态:
var n,i,j,p,tmp:longint; c,k:array[1..100]of longint; f:array[0..100,0..400]of boolean; begin readln(n); for i:=1 to n do readln(k[i],c[i]); f[0,0]:=true; for i:=1 to n do for j:=0 to 400 do if f[i-1,j] then for p:=0 to k[i] do begin tmp:=abs(j+(k[i]-p-p)*c[i]); if tmp<=400 then f[i,tmp]:=true; end; for i:=0 to 400 do if f[n,i] then begin writeln(i); break; end; end.
这种方法对于这道题的数据很吃香,最大的一个点只跑了20ms,考试时可以将以上两种方法并用,使得正确率最高!
相关文章推荐
- jvm 内存溢出的多种原因及优化方法
- [总结]树形依赖背包的优化方法
- cs231n 编程作业(2)学习心得——多种优化方法
- jzoj1501 糖果
- php多种优化方法总结
- JZOJ4844. 【GDOI2017模拟11.2】抗拒黄泉 背包合并相同状态优化容斥
- 【转】ListView多种类型优化,Java内存泄露的理解和解决,Handle造成内存泄露解决方法合集
- 报表性能优化方案之多种报表服务器内存修改方法
- poj 1014 Dividing(组合数学方法优化/多重背包问题+二进制优化)
- 多种方法巧妙优化数据库
- GSL Multidimensional Minimization: GSL的多维优化库使用方法介绍
- 树形依赖背包的优化方法
- 报表性能优化方案之多种报表服务器内存修改方法
- 内存溢出的多种原因及优化方法
- Day 1 jzoj1501. 糖果
- 内存溢出的多种原因及优化方法
- Shell获取字符串长度的多种方法总结
- 动态域名解析、端口映射等发布网站N种方法,多种发布网站方案
- SAP ABAP程序优化方法
- ListView异步加载图片方法和滚动优化