#洛谷 P3842 [TJOI2007]線段
阿新 • • 發佈:2020-07-14
P3842 [TJOI2007]線段
思路
1.出發點
顯然顯然顯然(重要的事情說三遍),這一行的終點可能是線段的左邊也可能是線段的右邊,所以要想走完這一行的線段,就需要從上一行的左端點或右端點,進而就有了下面的討論
2.討論
計f[0][i]為走完第i行的線段且
情況1:上一行線段在左,下一行線段在右
此時分兩種圖:
很顯然,這兩種可能可以合併
第一種圖:
圖1:
這裡指出一個誤區:明明線路\(2\)比線路\(1\)要優秀,為什麼還要考慮呢?其實很簡單,\(f[0][i-1]\)可能要比\(f[1][i-1]\)小很多,而不是\(f[1][i-1]\)一定大於\(f[0][i-1]\),所以\(f[0][i-1]+abs(r[i]-l[i-1])\)
圖2:
狀態轉移方程為:
f[0][i]=min(f[0][i-1]+abs(r[i]-l[i-1]),f[1][i-1]+abs(r[i-1]-r[i]))+len[i]+1;
f[1][i]=min(f[0][i-1]+abs(l[i-1]-l[i]),f[1][i-1]+abs(r[i-1]-l[i]))+len[i]+1;
第二種圖:
圖1:
圖2:
狀態轉移方程為:
f[0][i]=min(f[0][i-1]+abs(r[i]-l[i-1]),f[1][i-1]+abs(r[i-1]-r[i]))+len[i]+1; f[1][i]=min(f[0][i-1]+abs(l[i-1]-l[i]),f[1][i-1]+abs(r[i-1]-l[i]))+len[i]+1;
驚奇的發現,狀態轉移方程竟然一樣,這樣就減少判斷縮減了程式碼
情況2:上一行線段在中,下一行線段在中
此時也分兩種圖:
顯然這兩種也可以合併
同樣四張圖:
狀態轉移方程:
f[0][i]=min(f[0][i-1]+abs(r[i]-l[i-1]),f[1][i-1]+abs(r[i-1]-r[i]))+len[i]+1;
f[1][i]=min(f[0][i-1]+abs(l[i-1]-l[i]),f[1][i-1]+abs(r[i-1]-l[i]))+len[i]+1;
我們又要口矣了,兩種大情況的狀態轉移方程也一樣誒,顯然第三種情況(上一行線段在右,下一行線段在左)也是這個狀態轉移方程,都可以合併就不用判斷了誒誒誒(當然可以自己在畫畫第三種情況的圖)
程式碼:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2e4+5,INF=0x3f3f3f3f,mol=1000007;
int n,m,f[2][maxn],a[maxn],l[maxn],r[maxn],len[maxn];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int main(){
//freopen("a.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)l[i]=read(),r[i]=read(),len[i]=r[i]-l[i];
f[0][1]=r[1]-1+len[1];f[1][1]=r[1]-1;
for(int i=2;i<=n;i++){
f[0][i]=min(f[0][i-1]+abs(r[i]-l[i-1]),f[1][i-1]+abs(r[i-1]-r[i]))+len[i]+1;
f[1][i]=min(f[0][i-1]+abs(l[i-1]-l[i]),f[1][i-1]+abs(r[i-1]-l[i]))+len[i]+1;
}
cout<<min(f[0][n]+n-l[n],f[1][n]+n-r[n]);
}
OVER~