• 背包问题的回溯


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

  • 相关阅读:
    DataItem,gridview,repeater数据控件数据绑定
    repeater没有数据显示暂无数据,无记录
    Javascript操作Cookie的脚本 — CookieHelper
    <script>标签应该放到</body>标签之前
    IEnumerable 和 IQueryable 区别
    【three.js练习程序】鼠标滚轮缩放
    【three.js练习程序】动画效果,100个方块随机运动
    【three.js练习程序】随机生成100个方块
    树莓派练习程序(寻迹模块)
    树莓派练习程序(声音检测)
  • 原文地址:https://www.cnblogs.com/savennist/p/13628467.html
Copyright © 2020-2023  润新知