📜  门|门CS 2012 |问题 18(1)

📅  最后修改于: 2023-12-03 15:42:22.848000             🧑  作者: Mango

门|门CS 2012 |问题 18

该问题是一道经典的动态规划问题。下面是相应的详细介绍。

问题描述

有 $n$ 扇门排成一排,每扇门旁边有一个数字,代表该门需要花费的代价。在门的两侧有两个守卫,需要从一端走到另一端,每次只能经过相邻的两扇门(即要么从 $i$ 号门到 $i+1$ 号门,要么从 $i$ 号门到 $i-1$ 号门)。每次经过一扇门会花费与该门旁边数字相应的代价,相邻两次不能经过同一扇门。守卫可以从两端出发,问从两端出发的最小代价和是多少。

解决方案

我们可以使用动态规划来解决该问题。具体来说,我们可以使用 $f_{i,j,0/1}$ 表示从左/右端出发,经过前 $i$ 扇门且最后一步到达第 $j$ 扇门,且最后一步是向右/向左的最小代价和(当 $i=1$ 时,$f_{i,j,0/1}$ 表示从左/右端出发到达第 $j$ 扇门的最小代价和)。则有:

$$ f_{i,j,0/1}=\begin{cases} 0&\text{$i=1$ 且 $j=1$}\ +\infty&\text{$i=1$ 且 $j\neq 1$}\ \min_{k\in{1,2,\ldots,n}\backslash{j}}f_{i-1,k,1}+a_{j}&\text{$i>1$ 且 $j>1$}\ \min_{k\in{1,2,\ldots,n}\backslash{j}}f_{i-1,k,0}+a_{j}&\text{$i>1$ 且 $j<n$} \end{cases} $$

其中,$a_j$ 表示第 $j$ 扇门花费的代价。我们最终要求的则是:

$$ \min_{j\in{1,2,\ldots,n}}\min{f_{n,j,0},f_{n,j,1}} $$

代码实现

下面是对应的 C++ 实现代码,其中使用了优先队列(即堆)来加速处理:

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN=202;
const int INF=1e9;
priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>>,greater<pair<int,pair<int,int>>>> q;
int n,a[MAXN],f[MAXN][MAXN][2];
bool vis[MAXN][MAXN][2];
void update(int i,int j,int k,int x,int y,int z){
    if(f[i][j][k]>x){
        f[i][j][k]=x,q.push({x,{y,z}});
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    memset(f,0x3f,sizeof(f));f[1][1][0]=a[1],f[1][1][1]=a[1],vis[1][1][0]=vis[1][1][1]=true;
    q.push({a[1],{1,0}}),q.push({a[1],{1,1}});
    while(!q.empty()){
        int i=q.top().second.first,j=q.top().second.second,k=q.top().second.first%2,x=f[i][j][k];
        q.pop();if(vis[i][j][k]) continue;vis[i][j][k]=true;
        if(i==n) continue;
        update(i+1,j,0,x+a[i+1],i,j),update(i+1,j,1,x+a[i+1],i,j);
        if(j>1) update(i+1,j-1,1,x+a[j-1],i,j),update(i+1,j-1,0,x+a[i+1],i,j);
        if(j<n) update(i+1,j+1,0,x+a[j+1],i,j),update(i+1,j+1,1,x+a[i+1],i,j);
    }
    int ans=INF;
    for(int j=1;j<=n;j++) ans=min(ans,min(f[n][j][0],f[n][j][1]));
    cout<<ans<<endl;
    return 0;
}
完整代码及测试数据

完整代码可见:https://www.luogu.com.cn/problemnew/solution/p7902,其中包含了用于测试的数据。