LOJ2687 BalticOI2013 Vim 線頭DP


傳送門


多圖警告!!!

一種很新奇的\(DP\),全網似乎只有一兩篇題解……

首先,序列中的一段\(e\)等價於在跳的過程中這一段\(e\)之后的一個字符必須要經過,並且在最后的答案中加上$2 \times $e的個數。

那么原題等價於:給出一個序列和兩種移動方式,移動過程中必須要經過某一些點,求最小代價。

我們不妨把若干連續的\(f\)操作和若干連續的\(h\)操作看成線,那么移動路線就變成下面這樣

首先,考慮下面兩種移動路線

A路線一定沒有B路線優,因為A路線有重復的折返。

這樣說來:如果經過某些連續的\(f\)操作之后開始進行\(h\)操作,那么一定會到達要到達的最前面的目標,然后一直進行\(f\)操作不再回來。

到這里不難設計出一個暴力的\(DP\):設\(dp_{i,j}\)表示已經經過了前\(i\)個必經字符,當前光標在第\(j\)個字符時的最小代價。設字符集為\(A\),那么這種\(DP\)\(O(N^2A)\)的,不夠優秀。考慮優化。

發現上面的條件等價於對於某一個位置\(i\),經過的位置覆蓋了位置\(i\)\(i+1\)之間的線段的線的數量要么是\(1\),要么是\(3\),對應下圖的\(AB\)兩種情況。

到了這里就可以開始設計更加優秀的\(DP\)

\(p_{i,j}\)表示覆蓋了\(i\)\(i+1\)之間的線段\(1\)次,且覆蓋\(i\)\(i+1\)之間的線段的\(f\)操作選擇的字符是\(j\)的最小代價,\(q_{i,j,k}\)表示覆蓋了\(i\)\(i+1\)之間的線段\(3\)次,且在進行\(h\)操作之前覆蓋\(i\)\(i+1\)之間的線段的\(f\)操作選擇的字符是\(j\)、在進行\(h\)操作之后覆蓋\(i\)\(i+1\)之間的線段的\(f\)操作選擇的字符是\(k\)的最小代價

又設\(s_i\)表示字符串的第\(i\)個字符,\(imp_i\)表示原串中第\(i\)個字符前是否存在字符\(e\)

轉移:

\[\begin{align}p_{i,j} = & p_{i-1,j} & j \neq s_i \&\& imp_i \neq 1\\& p_{i-1,s_i} + 2 \\& q_{i-1,s_i,j} & j \neq s_i \\ & q_{i-1,s_i,s_i} + 2 \end{align} \]

\(p_{i,j}\)的轉移分別對應下圖的\(ABCD\)情況

其中虛線表示新加入的線,紅色字表示對應位置的字符類型,黑色字表示位置編號

\(\begin{align} q_{i,j,k} = & p_{i-1,j} + 3 & j \neq s_i \\ & p_{i-1,s_i}+5 \\ & q_{i-1,j,k} + 1 & j \neq s_i \&\& k \neq s_i \\ & q_{i-1,s_i,k} + 3 & k \neq s_i \\ & q_{i-1,j,s_i} + 3 & j \neq s_i \\ & q_{i-1,s_i,s_i} + 5 \end{align}\)

\(q_{i,j,k}\)轉移分別對應下圖中的\(ABCDEF\)情況

可以發現轉移就是把線延長和補全的過程,所以叫做線頭DP

初始值:\(f_{0,s_1}=0\),其他等於\(inf\)。最后的答案是\(f_{len,x}\),其中\(x\)是沒有在字符串中出現過的字符。這可以理解成在無限遠的地方有一個字符\(x\),最后一次操作就是直接跳到這一個無限遠的地方。當然,這意味着最后的答案會加上跳到這個無限遠的地方的\(2\)的代價,減掉\(2\)就行了。

Update:轉移\(q\)的時候並不知道為什么D有用,但是不轉移會WA

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;

inline int read(){
	int a = 0;
	char c = getchar();
	bool f = 0;
	while(!isdigit(c) && c != EOF){
		if(c == '-')
			f = 1;
		c = getchar();
	}
	if(c == EOF)
		exit(0);
	while(isdigit(c)){
		a = a * 10 + c - 48;
		c = getchar();
	}
	return f ? -a : a;
}

const int MAXN = 7e4 + 7 , A = 11;
int f[MAXN][A] , g[MAXN][A][A] , ch[MAXN];
bool must[MAXN];
int N , M , cnt;

inline char getc(){
	char c = getchar();
	while(!islower(c))
		c = getchar();
	return c;
}

int main(){
#ifndef ONLINE_JUDGE
	//freopen("in","r",stdin);
	//freopen("out","w",stdout);
#endif
	N = read();
	bool ife = 1;
	for(int i = 1 ; i <= N ; ++i){
		char c = getc();
		if(c == 'e')
			cnt += (ife = 1);
		else{
			must[++M] = ife;
			ife = 0;
			ch[M] = c - 'a';
		}
	}
	for(int i = 0 ; i < A ; ++i){
		for(int j = 0 ; j < A ; ++j)
			g[0][i][j] = INF;
		f[0][i] = INF;
	}
	f[0][ch[1]] = 0;
	for(int i = 1 ; i <= M ; ++i)
		for(int j = 0 ; j < A ; ++j){
			int t = INF;
			if(j != ch[i] && !must[i])
				t = min(t , f[i - 1][j]);
			t = min(t , f[i - 1][ch[i]] + 2);
			if(j != ch[i])
				t = min(t , g[i - 1][ch[i]][j]);
			t = min(t , g[i - 1][ch[i]][ch[i]] + 2);
			f[i][j] = t;
			for(int k = 0 ; k < A ; ++k){
				t = INF;
				if(j != ch[i])
					t = min(t , f[i - 1][j] + 3);
				t = min(t , f[i - 1][ch[i]] + 5);
				if(j != ch[i] && k != ch[i])
					t = min(t , g[i - 1][j][k] + 1);
				if(j != ch[i])
					t = min(t , g[i - 1][j][ch[i]] + 3);
				if(k != ch[i])
					t = min(t , g[i - 1][ch[i]][k] + 3);
				t = min(t , g[i - 1][ch[i]][ch[i]] + 5);
				g[i][j][k] = t;
			}
		}
	cout << f[M][10] + 2 * cnt - 2;
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM