Educational Codeforces Round 143 E. Explosions 【动态规划dp】

(Problem - E - Codeforces)

思路 :

我们可以定义两个dp数组,分别表示最高点为i向左能爆破的数量和向右的数量

那么dp(向左爆破的数量)应该是从i向左找到第一个j , 使得 A[i] - (i-j-1) > A[j],也就是说中间那部分一定可以使用等差数列计算,而小于j的部分则是dp[j]
那么我们可以推出 dp(i) = dp(j) + cost(j,i) (使用等差数列计算);
那么找第一个 满足 A[i] - (i-j-1) > A[j] 可以使用单调栈,保证找到最近的合法的下标
最大爆破数就等于max(dp[i] + dp1[j])
答案就是总和 - 总的爆破数

AC代码

#include
using namespace std;
const int N = 1e6+7;
typedef long long ll;
ll A[N];
int n;
ll dp[N],dp1[N],st[N],st1[N];
// dp表示左右的利润 
int top,top1;
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		cin>>n;
		top = top1 = 0;
		ll sum = 0;
		for(int i = 1;i<=n;i++){
			cin>>A[i];
			sum += A[i];
			dp[i] = dp1[i] = 0;
		}
		A[0] = 0;
		A[n+1] = 0;
		dp[0] = dp1[n+1] = 0;
		st[top++] = 0; 
		st1[top1++] = n+1;
		for(int i = 1;i<=n;i++){
			ll now = A[i];
			ll tot = 0; 
			while(top) {
				int p = A[st[top-1]];
				int ix = st[top-1];
				int change = max(0ll,now-i+ix+1);
				tot = (now + change)*(now-change+1)/2;
				if(change > p || ix == 0) break; 
				top--;
			} 
			int pre = st[top-1];
			st[top++] = i;
			dp[i] = dp[pre] + tot + A[pre] - A[i]; 	
		}
		ll ans = 0;
		for(int i = n;i>=1;i--){
			ll now = A[i];
			ll tot = 0;
			while(top1) {
				int p = A[st1[top1-1]];
				int ix = st1[top1-1];
				int change = max(0ll,now-ix+i+1);
				tot = (now + change)*(now-change+1)/2;
				if(change > p|| ix == n+1) break; 
				top1--;
			} 		
			int af = st1[top1-1];
			st1[top1++] = i;
			dp1[i] = dp1[af] + tot + A[af] - A[i]; 	
			ans = max(dp[i]+dp1[i],ans);
		}
		cout<<sum-ans<<'\n';
	}
	return 0;
}

你可能感兴趣的:(动态规划,动态规划,算法,图论)