UVa 624 CD && joj 1387 CD 背包问题

  题意很简单:n个东西,装入容量为c的背包,怎么使装的东西重量和最大。其实,就是价值等于重量的0/1背包问题。

  UVa 上规模比较小n最大值是20, 用DP把最大容量设置大点就可以AC,但是JOJ上n最大值是100,又没有告诉容量最大有多大,用DP一用就Runtime Error,在设置大点就Memory Limit Exceeded,只好用DFS搜索了。

  DFS搜索的剪枝还挺有技巧性的,其实,一般的讲分支限界法的算法书上都有。具体的剪枝,我都写在代码的注释里了。

 

DP代码:

 

 
  

#include
< fstream >
#include
< iostream >
using namespace std;

const int MAX = 104 ;

int m[MAX][MAX * MAX]; // m[i][j]是背包容量为j, 可选择物品为i, i+1, ... , n时, 01背包问题的最优值
int w[MAX]; // 重量=价值, 索引从1开始
int x[MAX]; // 解向量
int c; // 容量
int n;

int max( int a, int b) {
return (a > b ? a : b);
}

void knapsack() {
int i, j;
for (i = 0 ; i <= c; ++ i)
m[
0 ][i] = 0 ;
for (i = 0 ; i <= n; ++ i)
m[i][
0 ] = 0 ;
for (i = 1 ; i <= n; ++ i)
for (j = 1 ; j <= c; ++ j) {
if (j < w[i])
m[i][j]
= m[i - 1 ][j];
else
m[i][j]
= max(m[i - 1 ][j], m[i - 1 ][j - w[i]] + w[i]);
}
}

void backtrace() {
int max = c;
for ( int i = n; i >= 1 ; -- i) {
if (m[i][max] == m[i - 1 ][max])
x[i]
= 0 ;
else {
x[i]
= 1 ;
max
-= w[i];
}
}
}
int main() {
// ifstream cin("in");
while (cin >> c) {
cin
>> n;
for ( int i = 1 ; i <= n; i ++ ) {
cin
>> w[i];
}
knapsack();
backtrace();
for ( int i = 1 ; i <= n; ++ i){
if (x[i] == 1 )
cout
<< w[i] << " " ;
}
cout
<< " sum: " << m[n][c] << endl;
}
return 0 ;
}

 

 

 

DFS搜索代码:

 

 
  
#include < cstdio >
#include
< algorithm >
using namespace std;

const int MAX = 100 ;
int w[MAX]; // 重量=价值, 索引从1开始
int s[MAX]; // 从前向后的累积
int mc;
int c; // 容量
int n; // 磁道数量

void backtrack( int k, int cur) {
if (mc == c)
return ; // 已经达到最大容量
if (k < 0 )
return ;
if (cur + s[k] <= mc)
return ; // 把前面的都加上还没有现在的大
if (cur + w[ 0 ] > c)
return ; // 即使加上最小的也超了

int t = cur + w[k];
if (t <= c && t > mc)
mc
= t;
if (t <= c)
backtrack(k
- 1 , t);
backtrack(k
- 1 , cur);
}

int main() {
// freopen("in", "r", stdin);
while (scanf( " %d %d " , & c, & n) != EOF) {
for ( int i = 0 ; i < n; i ++ )
scanf(
" %d " , & w[i]);

sort(w, w
+ n); // 排序,使小的放在前面
s[ 0 ] = w[ 0 ];
for ( int i = 1 ; i < n; ++ i)
s[i]
= s[i - 1 ] + w[i];
mc
= 0 ;
backtrack(n
- 1 , 0 );
printf(
" sum:%d\n " , mc);
}
return 0 ;
}

 

转载于:https://www.cnblogs.com/liyongmou/archive/2010/07/09/1774039.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值