最大报销额--0-1背包

http://acm.hdu.edu.cn/showproblem.php?pid=1864

 深搜算法

代码
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 long s1,n,sum[31],max=0,n2;
5 float limit;
6
7
8 void order(int low,int high) ————快速排序,从小到大
9 {
10 long i,j;
11 i=low;
12 j=high;
13 while(i<j)
14 {
15 while(i<j&&sum[j]>=sum[i])j--;
16 if(i<j){
17 sum[0]=sum[j];
18 sum[j]=sum[i];
19 sum[i++]=sum[0];
20 }
21 while(i<j&&sum[i]<sum[j])i++;
22 if(i<j){
23 sum[0]=sum[i];
24 sum[i]=sum[j];
25 sum[j--]=sum[0];
26 }
27 }
28 if(i<high-1)order(i+1,high);
29 if(i>low+1)order(low,i-1);
30 }
31
32 void try1(int k,int m)
33 {
34 if(max==limit)return;
35 if(k<=n2+1&&max<m)max=m;
36 int i,j;
37 for(i=k;i<=n2-1;i++)
38 {
39 if(sum[i]+m<=limit)
40 try1(k+1,m+sum[i]);
41 else break;
42 }
43 }
44
45
46 int main(){
47
48 int i,j,flag;
49 char le;
50 int m;
51 float x;
52 int A,B,C;
53 fscanf("%f %d\n",&limit,&n);
54
55 while(n!=0)
56 {
57 limit*=100.0;
58 n2=1;
59 max=0;
60 for(j=1;j<=n;j++)
61 {
62 A=B=C=0;
63 flag=0;
64 fscanf(in,"%d ",&m);
65 for(i=1;i<=m;i++)
66 {
67 scanf("%c:%f ",&le,&x);
68 x*=100;
69 if(le=='A')A+=(int)x;
70 else if(le=='B')B+=(int)x;
71 else if(le=='C')C+=(int)x;
72 else flag=1;
73 }
74 if(A>60000||B>60000||C>60000)
75 flag=1;
76 else s1=A+B+C;
77 if(flag==0&&s1<=100000)sum[n2++]=s1; ————将可以报销的发票存下
78 }
79 order(1,n2-1);
80 try1(1,0);
81 printf("%.2f\n",(float)max/100);
82 scanf("%f %d\n",&limit,&n);
83 }
84 return 0;
85 }

 

在空间上更加优化,运用滚动数组:

代码
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 int n,nm,sum[31],max[2][3000100];
5 float limit1;
6 char le;
7
8
9 int main(){
10
11 int i,j,limit;
12 float x;
13 scanf("%f %d\n",&limit1,&n);
14
15 while(n!=0)
16 {
17 limit=(int)(limit1*100);
18 memset(sum,0,sizeof(sum));
19 for(i=1;i<=n;i++)
20 {
21 scanf("%d ",&nm);
22 int A=0,B=0,C=0,flag=0;
23 for(j=1;j<=nm;j++)
24 {
25 scanf("%c:%f ",&le,&x);
26 if(le=='A')A=A+(int)(x*100);
27 if(le=='B')B=B+(int)(x*100);
28 if(le=='C')C=C+(int)(x*100);
29 if(le!='A'&&le!='B'&&le!='C')flag=1;
30 }
31 sum[i]=A+B+C;
32 if(flag || A>60000 || B>60000 ||C>60000)
33 sum[i]=limit+1;
34 }
35
36 memset(max,0,sizeof(max));
37
38 for(j=1;j<=n;j++)
39 for(i=limit;i>=sum[j];i--)
40 if(max[(j-1)%2][i]<(max[(j-1)%2][i-sum[j]]+sum[j]))
41 max[j%2][i]=max[(j-1)%2][i-sum[j]]+sum[j];
42 else max[j%2][i]=max[(j-1)%2][i];
43
44 printf("%.2f\n",max[n%2][limit]/100.0);
45 scanf("%f %d\n",&limit1,&n);
46 }
47 return 0;
48 }

原始状态转移方程:max[j][i]=max{max[j-1][i],max[j-1][j-sum[i]]+sum[i]}

由于注意到了max[j]只与max[j-1]有关,所以直接将max[31][3000000]数组改为max[2][3000000],则状态

转移方程就变为:
max[j%2][i]=max{max[(j-1)%2][i],max[(j-1)%2][j-sum[i]]+sum[i]},省去了其他不必要的存储

 

 

直接只存一维,是更近一步的空间优化:

代码
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4  int n,nm,sum[31],max[3001000];
5  float limit1;
6 char le;
7
8 int main(){
9 int i,j,limit;
10 float x;
11 scanf("%f %d\n",&limit1,&n);
12 while(n!=0)
13 {
14 limit=(int)(limit1*100); 将limit1变为整数,复制给limit,注意强制类型转换
15 memset(sum,0,sizeof(sum));
16 for(i=1;i<=n;i++)
17 {
18 scanf("%d ",&nm);
19 int A=0,B=0,C=0,flag=0;
20 for(j=1;j<=nm;j++)
21 {
22 scanf("%c:%f ",&le,&x);
23 if(le=='A')A=A+(int)(x*100);
24 if(le=='B')B=B+(int)(x*100);
25 if(le=='C')C=C+(int)(x*100);
26 if(le!='A'&&le!='B'&&le!='C')flag=1;
27 }
28 sum[i]=A+B+C;
29 if(flag || A>60000 || B>60000 ||C>60000)
30 sum[i]=limit+1;
31 }
32 memset(max,0,sizeof(max));
33
34 for(j=1;j<=n;j++)
35 for(i=limit;i>=sum[j];i--)
36 if(max[i]<(max[i-sum[j]]+sum[j])) max[i]=max[i-sum[j]]+sum[j];
37 printf("%.2f\n",max[limit]/100.0);
38 scanf("%f %d\n",&limit1,&n);
39 }
40 return 0;
41 }

 max[limit]代表限制条件为limit下的最大值,与一般状态转移方程相比,少了一维,因为本次的状态只与上一次有关,
 所以就利用这种程序运行时的记忆性省去了表示第几张发票的那一维。

 

posted @ 2010-07-23 16:46  Danty  阅读(1194)  评论(0编辑  收藏  举报