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

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

 深搜算法

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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 }

 

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

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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]},省去了其他不必要的存储

 

 

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

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
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下的最大值,与一般状态转移方程相比,少了一维,因为本次的状态只与上一次有关,
 所以就利用这种程序运行时的记忆性省去了表示第几张发票的那一维。

 

转载于:https://www.cnblogs.com/aiyite826/archive/2010/07/23/1783859.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值