多阶段决策问题——DAG(算法竞赛入门经典笔记)

多阶段决策问题——DAG

本文为算法竞赛入门经典第九章第三节的笔记(刘汝佳. 算法竞赛入门经典.第2版[M]. 清华大学出版社, 2014.)
多阶段决策问题:每作一次决策就可以得到解的一部分,当所有决策做完之后,所有的解就“浮出水面”了。

多段图的最短路

单向TSP问题:

给定一个n*m的数字表格,找到一条从左到右的路径,使得上面的数字和最小。输出字典序最小的行号路径(每次可以从(i,j),走到(i,j+1),(i+1,j),(i-1,j)循环无限延伸没有边界)

思路:使用DAG上动态规划问题的基本思路。
1. 读入数据

        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                cin >> tsp[i][j];
            }
        }
  1. 使用记忆化搜索的方法
int dp(int i,int j){
    int& ans=dtsp[i][j];
    if(ytsp[i][j])
        return ans;
    if(j >= n-1){

        ans=tsp[i][j];
        ytsp[i][j]=1;
        return ans;
    }
    int mmin,next3[3];
    next3[0]=(i-1+m)%m;
    next3[1]=i;
    next3[2]=(i+1)%m;
    sort(next3,next3+3);
    ans=min(tsp[i][j]+dp(next3[0],j+1),tsp[i][j]+dp(next3[1],j+1))+1;
    for(int ii=0;ii<3;ii++){
        if(tsp[i][j]+dp(next3[ii],j+1) < ans){
            ans=tsp[i][j]+dp(next3[ii],j+1);
            retsp[i][j]=next3[ii];
        }
    }

    ytsp[i][j]=1;
    return ans;
}
  1. 打印输出字典序最小的策略
int eend = min(dtsp[0][0],dtsp[1][0])+1;
        int cur;
        for(int i=0;i<m;i++){
   
            if(dtsp[i][0] < eend){
   
                eend=dtsp[i][0];
                cur=i;
            }
        }
        cout << cur+1 ;
        for(int i=0;i<n-1;i++){
   

            cur = retsp[cur][i];
            cout << " " << cur+1;
        }

完整程序:

#define LOCAL
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
//#include <stdio.h>
#include <cstdlib>
#include<time.h>
#include <ctype.h>
#include <sstream>
#include <assert.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#define MAXN 92

using namespace std;

int tsp[11][101];


int m,n;

int dtsp[11][101];//记录从该点出发的深度
int ytsp[11][101];//记录该点是否被运算过
int retsp[11][101];//路径的记录
int dp(int i,int j){
    int& ans=dtsp[i][j];
    if(ytsp[i][j])
        return ans;
    if(j >= n-1){

        ans=tsp[i][j];
        ytsp[i][j]=1;
        return ans;
    }
    int mmin,next3[3];
    next3[0]=(i-1+m)%m;
    next3[1]=i;
    next3[2]=(i+1)%m;
    sort(next3,next3+3);
    ans=min(tsp[i][j]+dp(next3[0],j+1),tsp[i][j]+dp(next3[1],j+1))+1;
    for(int ii=0;ii<3;ii++){
        if(tsp[i][j]+dp(next3[ii],j+1) < ans){
            ans=tsp[i][j]+dp(next3[ii],j+1);
            retsp[i][j]=next3[ii];
        }
    }
    ytsp[i][j]=1;
    return ans;
}

int main()
{
    #ifdef LOCAL
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    #endif // LOCAL


    while(cin >> m >> n && m && n){
        memset(ytsp,0,sizeof(ytsp));
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                cin >> tsp[i][j];
            }
        }


        for(int i=0;i<m;i++){
            dp(i,0);
        }
        int eend = min(dtsp[0][0],dtsp[1][0])+1;
        int cur;
        for(int i=0;i<m;i++){
            if(dtsp[i][0] < eend){
                eend=dtsp[i][0];
                cur=i;
            }
        }
        cout << cur+1 ;
        for(int i=0;i<n-1;i++){

            cur = retsp[cur][i];
            cout << " " << cur+1;
        }
        cout << endl;
        cout<< eend << endl;
    }
  return 0;
}

0-1背包问题

物品无限的背包问题,和硬币问题一样,只需把无权图改成有权图即可。

物品有限的背包问题

只是凭“剩余体积”无法得知每个物品是否已经用过。
引入“阶段”,算法设计为:用 d

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值