例题UVA-624链接
题目是一个经典的背包问题,
但是有所区别的是,
如何在问题最后输出装入背包的各个物品的个数
当dp遍历完成后,在dp数组中,
状态转移方程本身产生了一个连续的路径,
这个路径强调了从各个物品是否被放入了背包。
追踪上述所说的这个路径,就能够回溯得到背包填充的全过程。
当然,这需要额外的同dp数组等规模的另一个父亲数组fa的维护。
下面我将通过这个例题,基本演示如何进行上述的回溯操作。
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
using namespace std;
//状态转移方程是什么?
int n,num;
const int maxn = 3000;
//dp[i][k]前i个物品进入容量为k的容器中所能放入的最大质量
int dp[maxn][maxn];
pair<int,int> fa[maxn][maxn];
int main(){
//freopen("in.txt","r",stdin);
while(cin>>n){
//数据输入
cin>>num;
vector<int> psd;
for(int i=0;i<num;i++){
int s; cin>>s;
psd.push_back(s);
}
//数据初始化
for(int i=0;i<=num;i++){dp[i][0]=0;fa[i][0]=make_pair(i,0);}
for(int i=0;i<=n;i++){dp[0][i]=0;fa[0][i]=make_pair(0,i);}
//01背包问题的经典解法
for(int i=1;i<=num;i++){
for(int k=1;k<=n;k++){
fa[i][k]=make_pair(i-1,k);
dp[i][k] = dp[i-1][k];
int p = k-psd[i-1];
if(p>=0 && dp[i-1][k]<dp[i-1][p]+k-p){//如果能够装入
fa[i][k]=make_pair(i-1,p);
dp[i][k]=dp[i-1][p]+k-p;
}
}
}
/*下面这些注释掉的代码可以帮助你更好的理解数组的组成*/
/*cout<<"=========="<<endl;
//输出fa数组
for(int i=0;i<=num;i++){
for(int k=0;k<=n;k++){
printf("{%3d,%3d}",fa[i][k].first,fa[i][k].second);
}
cout<<endl;
}
cout<<"=========="<<endl;
//输出dp数组
for(int i=0;i<=num;i++){
for(int k=0;k<=n;k++){
printf("%6d",dp[i][k]);
}
cout<<endl;
}*/
/*将回溯过程中经过的节点全部压入栈中*/
stack<pair<int,int> > od;
int x=num,y=n;
while(1){
od.push(make_pair(x,y));
if(fa[x][y]==make_pair(x,y))break;
int ax=fa[x][y].first; int ay=fa[x][y].second;
x = ax; y = ay;
}
//现在栈中是一条正确的背包填充路径
//下面遍历栈并依次检验各个物品放入背包的情况
pair<int,int> now = od.top(); od.pop();
while(!od.empty()){
pair<int,int> nxt = od.top(); od.pop();
if(now.second!=nxt.second)//注意这个物品入包的条件
cout<<psd[nxt.first-1]<<" ";
now = nxt;
}
cout<<"sum:"<<dp[num][n]<<endl;
}
return 0;
}
提供数据如下
5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2
正确输出
1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45
提交源码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
using namespace std;
int n,num;
const int maxn = 3000;
int dp[maxn][maxn];
pair<int,int> fa[maxn][maxn];
int main(){
while(cin>>n){
cin>>num;
vector<int> psd;
for(int i=0;i<num;i++){
int s; cin>>s;
psd.push_back(s);
}
for(int i=0;i<=num;i++){dp[i][0]=0;fa[i][0]=make_pair(i,0);}
for(int i=0;i<=n;i++){dp[0][i]=0;fa[0][i]=make_pair(0,i);}
for(int i=1;i<=num;i++){
for(int k=1;k<=n;k++){
fa[i][k]=make_pair(i-1,k);
dp[i][k] = dp[i-1][k];
int p = k-psd[i-1];
if(p>=0 && dp[i-1][k]<dp[i-1][p]+k-p){
fa[i][k]=make_pair(i-1,p);
dp[i][k]=dp[i-1][p]+k-p;
}
}
}
stack<pair<int,int> > od;
int x=num,y=n;
while(1){
od.push(make_pair(x,y));
if(fa[x][y]==make_pair(x,y))break;
int ax=fa[x][y].first; int ay=fa[x][y].second;
x = ax; y = ay;
}
pair<int,int> now = od.top(); od.pop();
while(!od.empty()){
pair<int,int> nxt = od.top(); od.pop();
if(now.second!=nxt.second)
cout<<psd[nxt.first-1]<<" ";
now = nxt;
}
cout<<"sum:"<<dp[num][n]<<endl;
}
return 0;
}
OK