1. 程式人生 > >POJ 3017 Cut the Sequence

POJ 3017 Cut the Sequence

題目連結

$O(n^2)$ 效率的 dp 遞推式:${ dp }_{ i }=min\left( dp_{ j }+\overset { i }{ \underset { x=j+1 }{ max }  } \left( { a }_{ x } \right)  \right) $,其中 $\sum _{ x=j+1 }^{ i }{ { a }_{ i } } \le m$。

嘗試著換一個角度看待這個問題,有一個序列 $a$,假設 $b_i$ 表示 $i$ 最左能擴充套件到 $b_i$ 位置,且 $[b_i, i]$ 的區間和不超過 $m$。

假設我們已經求得了 $dp_1$ 至 $dp_{i-1}$,現在需要求解 $dp_i$,先定義一個字尾最大值陣列,$max_i$ 表示 區間 $[1, i]$ 的最大值,通過觀察可以發現 $max_i$ 從後往前是非遞減的,且每一次的遞增位置都是出現了一個比先前所有數都大數,我們稱這種遞增位置為“變化位置”。

遞增位置大致可以用下圖表示($i$ 位置本身是第一個遞增位置):

想一下,如果我們選擇 2 號位置作為最後一段分割的最大值,那麼上一次分割的位置會在哪兒?上一段的最後一個位置肯定是 3 號變化位置,因為 dp 值是非遞減的。

同樣的,如果選擇 3 號位置作為最後一段分割的最大值,那麼上一段的最後一個元素肯定選擇 4 號變化位置。

也就是說計算 $i$ 位置的 dp 值,如果知道變化位置,我們只要列舉相鄰兩個變化位置,前一個的 dp 值加上後一個的 value 值,找到一個最小值就是 $dp_i$(注意最左邊的變化位置可以認為是 $b_i-1$)。

可惜,最壞的情況下依舊是 $O(n^2)$ 的,由於 POJ 資料水了,上面這樣的做法能水過去。

我們還是利用上述思路來求解答案,只不過增加一些優化。

事實上變化位置可以用單調佇列維護出來,維護變化位置的同時,將前一個的 dp 值加上後一個的 value 值也維護出來就可以了,維護值的話可以用 set 或者線段樹。

/*******************************
	Judge Result : AC 
 *******************************/

#include <cstdio>
#include <set>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn = 1e5 + 10;
const int INF = 0x7FFFFFFF;

long long a[maxn], sum[maxn], f[maxn];
int b[maxn];
int n;
long long m;

int q[maxn], first, last;
multiset<long long> st;
multiset<long long>::iterator it;

void Insert(int x) {
	last ++;
	q[last] = x;
	if(first == last) return;
	st.insert(f[q[last - 1]] + a[q[last]]);
}

void DeleteLast() {
	if(first == last) {
		last --;
		return;
	}
	it = st.find(f[q[last - 1]] + a[q[last]]);
	st.erase(it);
	last --;
}

void DeleteFirst() {
	if(first == last) {
		first ++;
		return;
	}
	it = st.find(f[q[first]] + a[q[first + 1]]);
	st.erase(it);
	first ++;
}

int main() {
#ifdef ZHOUZHENTAO
	freopen("test.in", "r", stdin);
#endif

	scanf("%d%lld", &n, &m);
	for(int i = 1; i <= n; i ++) {
		scanf("%lld", &a[i]);
		sum[i] = sum[i - 1] + a[i];
	}

	for(int i = 1; i <= n; i ++) {
		if(a[i] > m) {
			printf("-1\n");
			return 0;
		}
		int L = 1, R = i;
		while(L <= R) {
			int mid = (L + R) / 2;
			if(sum[i] - sum[mid - 1] <= m) R = mid - 1, b[i] = mid;
			else L = mid + 1;
		}
	}

	first = last = 0;
	q[first] = 1;
	f[1] = a[1];

	for(int i = 2; i <= n; i ++) {
		while(last - first + 1 > 0 && a[i] >= a[q[last]]) {
			DeleteLast();
		}
		while(last - first + 1 > 0 && q[first] < b[i]) {
			DeleteFirst();
		}
		Insert(i);
		f[i] = f[b[i] - 1] + a[q[first]];
		if(last - first + 1 > 1) {
			f[i] = min(f[i], *st.begin());
		}
	}
	printf("%lld\n", f[n]);
	return 0;
}

相關推薦

POJ 3017 Cut the Sequence

【題目連結】 $O(n^2)$ 效率的 dp 遞推式:${ dp }_{ i }=min\left( dp_{ j }+\overset { i }{ \underset { x=j+1 }{ max }  } \left( { a }_{ x } \right)  \right) $,其中 $\s

Cut the Sequence POJ

思路:dp[i]表示前第i項的最小值。 很容易寫出狀態轉移方程:dp[i]=min(dp[j]+max(a[j+1],a[i])),j<=i; 優化的思想是:我們需要快速的在前面找到一個j,同時還要找出j+1到i之間的最大項是什麼,這樣就能快速實現,最直接的思路是求

poj 3400 Dropping the stones

article using ring ace amp code == urn pac //next_permutation全排列 # include <stdio.h> # include <algorithm> # include <st

KMP中next的應用 POJ 2752 Seek the Name, Seek the Fame

baby body key out single clas ble numbers ask Seek the Name, Seek the Fame Time Limit: 2000MS Memory Limit: 65536K Total Submissi

HDU 5063 Operation the Sequence(暴力)

tracking pos sun 鏈接 char height ans wrap %d HDU 5063 Operation the Sequence 題目鏈接 把操作存下來。因為僅僅有50個操作,所以每次把操作逆回去執行一遍,就能求出在原來的數列中的位置。輸出就

poj 1426 Find The Multiple

lines code span cti its case sig pac unsigned id=1426">poj 1426 的傳送門 Language: Find The Multiple Time Limit: 1000MS Mem

POJ 1426 Find The Multiple &amp;&amp; 51nod 1109 01組成的N的倍數 (BFS + 同余模定理)

ase 正整數 ng- eof ger put emp lan respond Find The Multiple Time Limit: 1000MS Memory Limit: 10000K Total Submissio

POJ 1426 Find The Multiple(DFS,BFS)

ons pro sum 數字 there lin queue hat 一個數 Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal

POJ - 2387 Til the Cows Come Home

unique uic ring star eterm and string app lang Bessie is out in the field and wants to get back to the barn to get as much sleep as possi

POJ 3666 Making the Grade (DP)

我們 splay oat amp namespace sort 忽略 clu pre 題意:農夫約翰想修一條盡量平緩的路,路的每一段海拔是A_i,修理後是B_i,花費|A_i – B_i|,求最小花費。 思路:平緩的意思是海拔單調增或單調減(非嚴格),主要目的大概是讓我們做

POJ 2387 Til the Cows Come Home

tail from nal rail pst cows clas c代碼 == 題目連接: http://poj.org/problem?id=2387 Description Bessie is out in the field and wants to get back

POJ 1426 Find The Multiple(數論——中國同余定理)

定義 十進制 pro desc decimal tput one return solution 題目鏈接: http://poj.org/problem?id=1426 Description Given a positive integer n, write a pro

POJ 2151 Check the difficulty of problems:概率dp【至少】

統計 () and res -s code courier max space 題目鏈接:http://poj.org/problem?id=2151 題意:   一次ACM比賽,有t支隊伍,比賽共m道題。   第i支隊伍做出第j道題的概率為p[i][j].   問你所有隊

POJ 2983 Is the Information Reliable? 差分約束

memory math def alt pre cout fur 6.0 vector Time Limit: 3000MS Memory Limit: 131072K Total Submissions: 14143 Accepted: 4439

HDU 5783 Divide the Sequence

一段 type clas hdu 大於 pro 連續 php log http://acm.split.hdu.edu.cn/showproblem.php?pid=5783 題意:給出一段序列,現在要把它分成盡量多的連續序列,使得每一段序列之和都大於等於0。 思

POJ 3620 Avoid The Lakes【DFS找聯通塊】

cat nbsp info sample esc 網格 com accep memory Avoid The Lakes Time Limit: 1000MS Memory Limit: 65536K Total Submissions:

[Poj] Roads in the North

for org poi main eof spa esp col etc http://poj.org/problem?id=2631 樹的直徑裸題 dfs/bfs均可 /* dfs */ #include <iostream> #in

POJ 1426 -- Find The Multiple

二叉搜索樹 center pan include false esp while 搜索 amp POJ 1426 -- Find The Multiple 大致題意: 給出一個整數n,(1 <= n <= 200)。求出任意一個它的倍數m,要求m必須只由十進制的

POJ 2752 Seek the Name, Seek the Fame(KMP求公共前後綴)

字符串 iostream 字符 org 所有 對稱性 char can for 題目鏈接:http://poj.org/problem?id=2752 題目大意:給你一串字符串s找到所有的公共前後綴,即既是前綴又是後綴的子串。 解題思路: 如圖所示 假設字

【叠代博弈+搜索+剪枝】poj-1568--Find the Winning Move

() spa class 勝利 hid iat media gif nes poj 1568:Find the Winning Move 【叠代博弈+搜索+剪枝】 題面省略。。。 Input The input contains one or more test c