連續段DP

HarlemBlog發表於2024-10-15
更新日誌

概念

這個DP主要用於解決類似於“在一個數列中產生一個個連續塊,求可能數”的動規問題。

比如,在一排連續的盒子中放蘋果,連著的蘋果都可視作一個連續塊。

思路

一般來說,我們都會考慮新加入的元素對原狀態的影響。通常的,有三種情況:

  • 加入的元素在原先某個連續段的兩端。
  • 加入的元素單獨成為一個連續段。
  • 加入的元素把兩個連續段連成了一個。

值得注意的是,通常不考慮連續段的具體狀態,而只考慮整體狀態,如連續塊數量與元素數量。

究其原因,是因為\(存在即合理\)

更具體地,之所以如此,是因為當前情況必然由之前情況轉移而來,而非轉移到下一個情況去,所以無需擔心這種情況會不會發生的疑問。

只要存在合法的上一種情況,這種情況就必然可以存在。(當然前提是狀態轉移方程沒錯哈哈哈)

只要這種情況不合法,那麼就不可能存在合法的上一種情況,或者說不可能從不可能的方式轉移過來。(舉例子:無需擔心情況二插入空間不夠的問題,或者由方法一轉移來但是事實上合併了兩個連續段的情況。)

公式

\(f_{i,j}\)表示有\(i\)個元素,\(j\)個連續塊的情況

\[f_{i,j}= \begin{cases} 2j·f_{i-1,j} \\ j·f_{i-1,j-1} \\ (j-1)·f_{i-1,j+1}\\ \end{cases} \]

三種情況分別對應思路部分中三種。

細節講解:

  • 情況一之所以要\(×2j\),是因為有\(j\)個連續段可供選擇,每一個都可以選擇兩段之一。
  • 情況二\(×j\),是因為有\(j\)個空隙可以插入新的連續段。
  • 情況三同情況二類似,但是不能插入到兩端的空隙中,只能插到兩個連續段中間。

例題

不死鳥與電腦

程式碼

前注:非題解,不做詳細講解

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

#define fir first
#define sec second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
typedef pair<int,ll> pil;
typedef pair<ll,ll> pll;
typedef pair<int,pii> pip;
typedef vector<int> veci;
typedef vector<pii> vecp;
typedef priority_queue<int> bghp;
typedef priority_queue<int,vector<int>,greater<int> > lthp;
typedef priority_queue<pli,vector<pli>,greater<pli> > prhp;

const int mod=998244353;
const int N=1e4+5;

int n,m,k;
int r[N],c[N];
veci rs[N],cs[N];
vecp ed[N];
bool pd;

int dis[N];
bool vis[N];
void dij(){
    prhp pq;
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    pq.push({0,1});
    while(!pq.empty()){
        pli tp=pq.top();pq.pop();
        int now=tp.sec;
        if(vis[now])continue;
        vis[now]=true;
        for(int nxt=1;nxt<=k;nxt++){
            if(vis[nxt])continue;
            if((r[now]==r[nxt]&&abs(c[now]-c[nxt])==1||c[now]==c[nxt]&&abs(r[now]-r[nxt])==1)){
                if(dis[nxt]>dis[now]){
                    dis[nxt]=dis[now];
                    pq.push({dis[nxt],nxt});
                }
            }
            else if(abs(c[now]-c[nxt])<=2||abs(r[now]-r[nxt])<=2){
                if(dis[nxt]>dis[now]+1){
                    dis[nxt]=dis[now]+1;
                    pq.push({dis[nxt],nxt});
                }
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m>>k;
    int en;
    for(int i=1;i<=k;i++){
        cin>>r[i]>>c[i];
        if(r[i]==n&&c[i]==m){
            pd=true;
            en=i;
        }
    }
    dij();
    if(!pd){
        en=k+1;
        for(int i=1;i<=k;i++){
            if(n-r[i]<=1||m-c[i]<=1)dis[en]=min(dis[en],dis[i]+1);
        }
    }
    if(dis[en]==0x3f3f3f3f)cout<<-1;
    else cout<<dis[en];
    return 0;
}

相關文章