更新日誌
概念
這個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;
}