CF229D 動態規劃dp

因爲我菜所以只能想到消耗空間大,時間複雜度差的做法但是我就是要發!!

首先這種題需要知道他是個dp(看不出來就GG),然後思考下怎麼做,首先他是會進行區間合併,並且可以進行連續的合併,也就是把這個過程看做是一個連續區間合併的過程
例:
123456
[1,2] [3,4,5] [6]
可以將一個序列分解爲若干個區間(區間長度可以爲1),但是區間必須是連續的,不能說你1和3去合併在一起,這是不允許的。
知道他是這樣一個過程後要幹嘛呢,我們可以設計dp轉態
設 f[i][j] 表示[1,i]的序列都合法後,第i個與前j個位置一起合併的最少次數,也就是區間[i-j+1, i]作爲一個連續區間。
爲什麼是這樣子記錄呢,因爲在dp中一個很常見的做法的第二維度記錄的是第i個位置所代表的權值。但是在這道題裏的問題是權值的取值範圍是取決於輸入數據,即使離散化後這個權值的取值範圍也可能達到一個很大的量級,這會導致我們的空間炸掉。
所以就有了上述的狀態轉移方程,同樣是記錄那些狀態,但是我們換了種表示方法。

接下來思考轉移
我是區間[i-j+1, i]作爲一個連續區間,那麼我這個區間的權值是固定的,上一個區間的最後一個位置是i-j,那麼我們在i-j所在區間是可以獲取信息,i-j這個位置往前合併了1個,2個,3個。。。。。這個大小取決於什麼時候拿的權值超過當前i的區間所代表的權值就不行了。然後觀察每個位置的高都大於1,區間權值具有單調性。我們就對於i-j這個位置最多可以合併多少個位置是可以二分出來的。然後在先前記錄一個答案的前綴min,就可以二分後O1的到答案。
複雜度就是O(n^2logn)

int N,f[max_][max_],sum[max_],value[max_][max_];
il int getsum(int L, int R) {
	return sum[R] - sum[L - 1];
}
il void update(int x) {
	value[x][0] = inf;
	for (int i = 1; i <= x; i++) {
		value[x][i] = min(value[x][i - 1], f[x][i]);
	}
}

il int getv(int weizhi, int ht) {
	if (weizhi == 0)return 0; 
	if (getsum(weizhi, weizhi) > ht)return inf;
	int L = 1, R = weizhi;
	while (L < R){
		int mid = ((L + R) >> 1) + 1;
		//[1,mid]//[weizhi,weizhi - mid + 1]
		if (getsum(weizhi - mid + 1,weizhi) <= ht)L = mid;
		else R = mid - 1;
	}
	//[1,R]
	return value[weizhi][R];
}
signed main() {
	N = read();
	for (int i = 1; i <= N; i++) {
		sum[i] = read();
		sum[i] += sum[i - 1];
	}
	memset(f, 127, sizeof(f)); memset(value, 127, sizeof(value));
	f[1][1] = 0; update(1);
	for (int i = 2; i <= N; i++) {
		for (int j = 1; j < i; j++) {
			//[i-j+1,i]
			f[i][j] = getv(i - j, getsum(i - j + 1, i)) + j - 1;
		}
		f[i][i] = i - 1;
		update(i);
	}
	int ans = inf;
	for (int i = 1; i <= N; i++) {
		ans = min(ans, f[N][i]);
	}
	printf("%d", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章