背包问题的回溯

例题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

原文地址:https://www.cnblogs.com/savennist/p/13628467.html