[jzoj]1501. 糖果(优化多维背包的多种方法)
来源:互联网 发布:pkpm计算软件 编辑:程序博客网 时间:2024/05/29 04:00
一般来说,求解多维背包是求最小价值,但是也可以求其他的,例如此题:
多维背包复杂度O(nmk),但有很多的优化方法.
Link
https://jzoj.net/senior/#contest/show/1992/2
Problem
给定n个背包的重量ci及个数ki,使其分成两堆使得差最小.
Data constraint
50%的数据1<=N,
100%的数据1<=N<=100,1<=
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
注意到
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
我们再来两个优化:
我们把
当然,还有一个更加强劲的优化,我们判断如果当前可行的最优解已经产生,那么就没必要继续进行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,考试时可以将以上两种方法并用,使得正确率最高!
阅读全文
1 0
- [jzoj]1501. 糖果(优化多维背包的多种方法)
- hdu4501(多维背包)
- Medicine (01多维背包)
- 【bzoj 4548】【JZOJ 5229】 小奇的糖果
- 【JZOJ 5229】【GDOI2018模拟7.14】小奇的糖果
- 多维转一维的方法
- php多种优化方法总结
- 多种方法巧妙优化数据库
- 多种背包(HDU 1059)
- HDU_3535_AreYouBusy(多种背包组合)
- 新背包(多种做法)
- 多维多选的背包问题
- SQL Server数据库优化的10多种方法
- SQL Server数据库优化的10多种方法
- SQL Server数据库优化的10多种方法
- 内存溢出的多种原因及优化方法
- jvm 内存溢出的多种原因及优化方法
- 内存溢出的多种原因及优化方法
- Google后Hadoop时代的新“三驾马车”——Caffeine、Pregel、Dremel
- 小程序页面间传值
- 整数的划分数
- 深入分析ConcurrentHashMap
- 打开windows远程连接服务端方法
- [jzoj]1501. 糖果(优化多维背包的多种方法)
- 堆与栈的区别
- IQ使命2 Barcelona 巴塞罗那(色块覆盖)攻略
- 详解ShellShock 漏洞复现原理,内附ShellShock的修复方法
- EventTrigger UGUI事件 点击,拖拽,进入,离开
- Required content for platform tvOS Simulator is missing. unable to find utility "git", not a develop
- HttpUrlConnection和HttpClient两种方式获取GET和POST请求
- ubantu下安装f.lux
- Oracle 11.2.0.1.0中降序索引的bug