出自算法引论-一种创造性方法
1、背包问题:
给定一个整数K和N个大小不同的物品,第i个物品的体积为S[i],寻找一个子集使子集的和大小等于K,或者确定不存在这样的子集
解答:
动态规划,即建立P[n][k](0<=n<=N,0<=k<=K)大小二维数组保存解,根据前面的解P[n-1][k],P[n-1][K-S[n]]的状态来确定。
代码:
const int N = 6;//物品数量
int S[N+1] = {0,3,5,6,8,2,4};//物品体积,S[0]无效
const int K = 14;//背包大小
typedef struct _state{
int exist;//是否存在解
int belong;//是否包含当前S[n]
}state;
state P[N+1][K+1];
int knapsack()
{
P[0][0].exist = 1;
std::cout<<" 0 ";
for (int k = 1; k <= K; k++){
P[0][k].exist = 0;
std::cout<<k<<" ";
}
std::cout<<std::endl;
for (int i = 1; i <= N; i++){
std::cout<<S[i]<<" ";
for (int k =0; k <= K;k++){
P[i][k].exist = 0;
if (P[i-1][k].exist){
P[i][k].exist = 1;
P[i][k].belong = 0;
}
else if (k-S[i]>=0){
if (P[i-1][k-S[i]].exist){
P[i][k].exist = 1;
P[i][k].belong = 1;
}
}
if (P[i][k].belong && P[i][k].exist) std::cout<<"I ";
else if(P[i][k].exist) std::cout<<"O ";
else std::cout<<"- ";
if (k>=10)
std::cout<<" ";
}
std::cout<<std::endl;
}
return P[N][K].exist;
}
2、习题问题:令x1,x2,。。。xn是一组整数,试找到一个划分这个数组为2部分相等的方法,或者不能做出这样的划分
解答:与背包一样的解法,只是此时背包K的值为(x1+x2+...+xn)/2
代码:
int knapsack_for_sum_mod_2()
{
P[0][0].exist = 1;
std::cout<<" 0 ";
for (int k = 1; k <= K; k++){
P[0][k].exist = 0;
std::cout<<k<<" ";
}
std::cout<<std::endl;
for (int i = 1; i <= N; i++){
std::cout<<S[i]<<" ";
for (int k =0; k <= K;k++){
P[i][k].exist = 0;
if (P[i-1][k].exist){
P[i][k].exist = 1;
P[i][k].belong = 0;
}
else if (k-S[i]>=0){
if (P[i-1][k-S[i]].exist){
P[i][k].exist = 1;
P[i][k].belong = 1;
}
}
if (P[i][k].belong && P[i][k].exist) std::cout<<"I ";
else if(P[i][k].exist) std::cout<<"O ";
else std::cout<<"- ";
if (k>=10)
std::cout<<" ";
}
std::cout<<std::endl;
}
//打印解
int n = N;
int k = K;
while(n>=1 && k>=0){
if (P[n][k].exist){
if (P[n][k].belong){
std::cout<<S[N]<<" ";
}
else{
for (; n >=1; n--){
if (P[n][k].belong){
std::cout<<S[n]<<" ";
k-=S[n];
break;
}
}
}
}
else
break;
}
std::cout<<std::endl;
return P[N][K].exist;
}