noj [1480] 懒惰的风纪委Elaine (多重背包)
http://ac.nbutoj.com/Problem/view.xhtml?id=1480
-
[1480] 懒惰的风纪委Elaine
- 时间限制: 1000 ms 内存限制: 65535 K
- 问题描述
-
Elaine是学园都市中的一个风纪委,每天都会接到命令对某个街道进行检查,并抓捕危险分子。她所在的风纪委支部附近有M条街道。这些街道由北到南并排均匀的分布在一条直线上,每条街道之间的距离都为1。但是众所周知,Elaine是一个很懒很懒的人(-..-说我坏话!!被我看到了!!),她不想一步一步走完所有街道,但好在她的好友Kuso为她制作了大量的传送卷轴。不过,因为Kuso的能力等级太低,他制作的卷轴有严重的缺点,他的卷则只能向南飞一段固定的距离(当然,他预先制作了很多种类的卷轴),而Elaine所在的风纪委支部却在最北边。每一次出去检查,Elain都要使用好几张卷轴。但如果是某些不能传送到的地方,Elaine只能走过去了。不过回来的话,她就可以用自带的传送系统传送到支部的传送点。
有一天,Elaine想知道,如果她从风纪委支部出发,可以检查那些街道。她手里有N种传送卷轴(1,2,3,,,N),每个卷轴可以传送的距离为Ai,卷轴的数量为Ci。则Elaine靠那些卷轴,可以不走路而直接传送的有哪些街道?
- 输入
-
数据有多组输入。每一组数据的第一行有两个数:N,M(0<N<=100,0<M<=1000)。分别表示传送卷轴的种类数和街道数量。在第二行有2N个数,A1,A2,A3...An,C1,C2,C3...Cn (1<=Ai<=100000,1<=Ci<=1000)。数据输入以0 0结束。
- 输出
-
每组数据占一行,输出一个数,为Elaine可以传送到的街道总数。
- 样例输入
-
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
- 样例输出
-
8 4
- 先将多重背包转换成01背包,然后在求解01背包:
- 任何一个正整数N 都可以分解成 N = 2^0 + 2^1 + 2^2+......+(剩下不足2^n的部分)
- 例如:13 = 1 + 2 + 4 + 6
- 这里是对数量进行拆分; 这样的好处可以很好的降低复杂度,从O(N*N)降到O(N*logN)
-
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 using namespace std; 6 7 int dp[1010]; 8 int a[110]; 9 vector<int> v; 10 11 int main() 12 { 13 int n,m; 14 while(scanf("%d%d",&n,&m)&&n&&m) 15 { 16 17 int i,j; 18 for(i=0;i<n;i++) 19 { 20 scanf("%d",&a[i]); 21 } 22 23 //多重背包转换成01背包 24 v.clear(); 25 int t; 26 for(i=0;i<n;i++) 27 { 28 int nc; 29 scanf("%d",&nc); 30 t=1; 31 while(t<=nc) 32 { 33 v.push_back(t*a[i]); 34 nc-=t; 35 t=t*2; 36 } 37 if(nc>0){ 38 v.push_back(nc*a[i]); 39 } 40 } 41 42 //01背包求解过程 43 memset(dp,0,sizeof(dp)); 44 dp[0]=1; 45 int sum=0; 46 for(i=0;i<v.size();i++) 47 { 48 for(j=sum==m?m-v[i]:sum;j>=0;j--) //这个用到优化j=sum==m?m-v[i]:sum,这个优化是必要的,不然TLE 49 { 50 if(dp[j]!=0&&j+v[i]<=m){ 51 dp[j+v[i]]=1; 52 } 53 } 54 sum+=v[i]; 55 if(sum>m) 56 { 57 sum=m; 58 } 59 } 60 int cnt=0; 61 for(i=1;i<=m;i++) 62 { 63 if(dp[i]) 64 { 65 cnt++; 66 } 67 } 68 printf("%d\n",cnt); 69 } 70 return 0; 71 }