UVA_1025 a Spy in the Metro 有向无环图的动态规划问题

应当认为,有向无环图上的动态规划问题是动态规划的基本模型之一,对于某个模型,如果可以转换为某一有向无环图的最长、最短路径问题,则可以套用动态规划若干方法解决。

原题参见刘汝佳紫薯267页。

在这个题目中,首先将整个模型规划成为有向无环图的模式:
1,对于某小特工,于j时间处在在第i站,可以成为一个独立的状态,也就是有向无环图的一个节点。

2,对于每个节点,可能能够走得有三个不同的边——坐火车往左走,进入左边的某个状态;坐火车往右走,进入右边的某个状态;原地等待,进入该站点的下一个时间。

每条边,拥有权重——坐火车边权位0,但是原地等待边权位1.,由此,可以将动态规划的问题描述成为一个有向无环图上的寻路问题。直觉上至少可以使用最短路算法。

但是对于有向无环图情况,可以进行特殊的优化:使用直接刷表法求得。

之前,我们大多使用“对于每个节点进行扫描的时候更新该节点的所有子节点中某值,从而使得,该节点得到最优解”。但是很多时候,并不一定可以使用这种思路来进行很直观的赋值。在这种情况下,我们可以退而求其次,通过若干次计算,在到达某一节点之后“更新”可能直接到达的节点的边权,从而得到,我们需要的最值。

这道题在做的时候,感觉到有些比较深的坑——例如初始化的故事。

AC代码:

#include<bits/stdc++.h>
using namespace std;

const long long MAXN=233;
const long long INF=1e9+233;
long long n,t,m1,m2;
long long ti[MAXN];
long long d1[MAXN];
long long d2[MAXN];
bool check[MAXN][MAXN][2];
long long dp[MAXN][MAXN];

long long ca=1;
void init()
{
    memset(check,0,sizeof(check));
    memset(ti,0,sizeof(ti));
    memset(d1,0,sizeof(d1));
    memset(d2,0,sizeof(d2));
    cin>>t;
    for(int i=0;i<n-1;++i)    cin>>ti[i];
    cin>>m1;
    for(int i=0;i<m1;++i)    cin>>d1[i];
    cin>>m2;
    for(int i=0;i<m2;++i)    cin>>d2[i];
    check[0][d1[0]][0]=1;check[n-1][d2[0]][1]=1;
    
    for(int i=0;i<MAXN;++i)
    {
        for(int j=0;j<MAXN;++j)dp[i][j]=INF;
    }long long time=0;
    for(int i=0;i<n-1;++i)
    {
        
        for(int j=0;j<m1;++j)
        {
            check[i][d1[j]+time][0]=1;
//            cout<<"left: "<<i<<ends<<d1[j]+time<<endl;
        }time+=ti[i];
    }
    time=0;
    for(int i=n-1;i;--i)
    {
        time+=ti[i];
        for(int j=0;j<m2;++j)
        {
            check[i][d2[j]+time][1]=1;
//            cout<<"right: "<<i<<ends<<d2[j]+time<<endl;
        }
    }dp[0][0]=0;
    for(int j=0;j<=t;++j)
    {
        for(int i=0;i<n;++i)
        {
            if(dp[i][j]>=INF)continue;
            dp[i][j+1]=min(dp[i][j]+1,dp[i][j+1]);
            if(i<n-1&&check[i][j][0])dp[i+1][j+ti[i]]=min(dp[i][j],dp[i+1][j+ti[i]]);//,cout<<"check_left "<<i<<ends<<j<<ends<<dp[i][j]<<endl;
            if(i&&check[i][j][1])dp[i-1][j+ti[i-1]]=min(dp[i][j],dp[i-1][j+ti[i-1]]);//,cout<<"check_right "<<i<<ends<<j<<ends<<dp[i][j]<<endl;
        }
    }
    cout<<"Case Number "<<ca++<<": ";
    if(dp[n-1][t]<INF)cout<<dp[n-1][t]<<"
";
    else cout<<"impossible
";
    
}


int main()
{
    cin.sync_with_stdio(false);
    while(cin>>n&&n)init();
    
    return 0;
}
原文地址:https://www.cnblogs.com/rikka/p/7448262.html