給你一張n*m個的表格 和k個點有寶藏, 以及只有b列的位置可以往上走,在其他列只能往左或者往右走,每次只能往左往右或者往上選擇一個方向走一格,問從(1,1)找到所有寶藏最少需要多少步
做法: 考慮走完每層所有有寶藏的時候,必然是位於最左邊有寶藏的位置和最右邊有寶藏的位置
設dp[i][0], dp[i][1] 分別爲從1到第i層找到所有寶藏後位於最左邊寶藏的位置時的最短路徑和最右邊寶藏位置的最短路徑
設sum(u,du,v,dv) 爲第u層du位置 到 第v層dv位置的最短距離
找最短距離的時候找到第u層du寶藏的位置 前面最近的一個通道的位置和後面最近的一個通道的位置,則最短距離爲該點到通道然後通道到v,dv^1的寶藏的距離再加上(v,dv) 和(v,dv^1)的距離
注意lower_bound 找前面最近的位置-b 後要判斷<=q
upper_bound找後面最近的位置-b-1 後要判斷>0 防止數組越界
那麼我們可以得到dp方程
則從j=1開始 我們i=2開始往上找 跳過沒有寶藏的層,每次更新後把j設置爲該層然後去找下一個i層 最後答案爲最後一層左右寶藏位置的dp值取min
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200005;
const int inf=0x3f3f3f3f;
int n,m,k,q;
int b[maxn];
ll mp[maxn][2];
ll dp[maxn][2];
vector<int>vec;
ll sum(int u,int du,int v,int dv)
{
ll r=inf;
int p=lower_bound(b+1,b+1+q,mp[u][du])-b;
if(p<=q)r=abs(b[p]-mp[u][du])+abs(b[p]-mp[v][dv^1])+abs(mp[v][dv^1]-mp[v][dv]);
int t=upper_bound(b+1,b+1+q,mp[u][du])-b-1;
if(t)r=min(r,abs(b[t]-mp[u][du])+abs(b[t]-mp[v][dv^1])+abs(mp[v][dv^1]-mp[v][dv]));
return r;
}
//dp[i][0]=min(dp[j][0]+sum(j,0,i,0)+j-i,dp[i][0]);
//dp[i][0]=min(dp[j][1]+sum(j,1,i,0)+j-i,dp[i][0]);
//dp[i][1]=min(dp[j][0]+sum(j,0,i,1)+j-i,dp[i][1]);
//dp[i][1]=min(dp[j][1]+sum(j,1,i,1)+j-i,dp[i][1]);
int main()
{
scanf("%d%d%d%d",&n,&m,&k,&q);
ll r,c;
for(int i=1;i<=n;i++)mp[i][0]=inf,mp[i][1]=-inf;
for(int i=1;i<=k;i++)
{
scanf("%lld%lld",&r,&c);
mp[r][0]=min(mp[r][0],c);
mp[r][1]=max(mp[r][1],c);
//vec.push_back(r);
}
mp[1][0]=1,mp[1][1]=max(1ll,mp[1][1]); //把(1,1)當做寶藏
for(int i=1;i<=q;i++)scanf("%d",&b[i]);
sort(b+1,b+1+q);
//sort(vec.begin(),vec.end());
memset(dp,0x3f,sizeof(dp));
dp[1][1]=(ll)abs(mp[1][1]-1);
dp[1][0]=(ll)abs(mp[1][1]-1)+abs(mp[1][1]-mp[1][0]);
int j=1;
for(int i=2;i<=n;i++)
{
if(mp[i][0]==inf)continue;
dp[i][0]=min(dp[j][0]+sum(j,0,i,0)+i-j,dp[i][0]);
dp[i][0]=min(dp[j][1]+sum(j,1,i,0)+i-j,dp[i][0]);
dp[i][1]=min(dp[j][0]+sum(j,0,i,1)+i-j,dp[i][1]);
dp[i][1]=min(dp[j][1]+sum(j,1,i,1)+i-j,dp[i][1]);
//mi=min(dp[i][1],dp[i][0]);
j=i;
}
printf("%lld\n",min(dp[j][0],dp[j][1]));
return 0;
}