Codeforces713C Sonya and Problem Wihtout a Legend(DP)

题目

Source

http://codeforces.com/problemset/problem/713/C

Description

Sonya was unable to think of a story for this problem, so here comes the formal description.

You are given the array containing n positive integers. At one turn you can pick any element and increase or decrease it by 1. The goal is the make the array strictly increasing by making the minimum possible number of operations. You are allowed to change elements in any way, they can become negative or equal to 0.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 3000) — the length of the array.

Next line contains n integer ai (1 ≤ ai ≤ 109).

Output

Print the minimum number of operation required to make the array strictly increasing.

Sample Input

7
2 1 5 11 5 9 11
5
5 4 3 2 1

Sample Output

9
12

分析

题目打给说给一个序列,可以进行若干次操作,每次操作选择一个数让它+1或-1,问最少要几次操作使得序列变为严格单调递增序列。

首先考虑不是严格递减的情况,这其实是个经典问题的样子,太弱都没做过。。

  • 不严格递减最后修改完成后的各个数一定是原序列中的某一个数

这个大概可以这么理解:原序列,从左到右扫过去,如果左边的大于右边的,要嘛左边的减掉使其等于右边的,要嘛右边的加上使其等于左边的。

于是就可以考虑用dp来做了:

  • dp[i][j]表示序列前i个数都单调递增且第i个数更改为不大于原序列中第j个数的最少代价
  • 转移就是dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i],b[j])),a是原序列,b是原序列排序去重的序列

回到原问题,原问题是求严格单调递增,这个可以转化成不严格单调递增:

  • 让原序列每个数a[i]减去i

这样转化可行是因为:

这个转化感觉挺神奇的。。

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
long long d[3333][3333],a[3333],b[3333];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; ++i){
		scanf("%d",a+i);
		a[i]-=i;
		b[i]=a[i];
	}
	sort(b+1,b+1+n);
	int bn=unique(b+1,b+1+n)-b-1;
	memset(d,127,sizeof(d));
	for(int i=1; i<=bn; ++i){
		d[1][i]=min(d[1][i-1],abs(a[1]-b[i]));
	}
	for(int i=2; i<=n; ++i){
		for(int j=1; j<=bn; ++j){
			d[i][j]=min(d[i][j-1],d[i-1][j]+abs(a[i]-b[j]));
		}
	}
	printf("%lld",d[n][bn]);
	return 0;
}
时间: 2024-09-30 04:32:55

Codeforces713C Sonya and Problem Wihtout a Legend(DP)的相关文章

CF 713C Sonya and Problem Wihtout a Legend(DP)

原版题意:给定一个序列,每次操作给其中一个数$+1$或$-1$,问最少需要多少操作使得整个序列为不下降序列. 题解:不下降序列最后修改完成后的各个数一定是原序列中的某一个数.关于这个的理解看到网上的一个解释:原序列,从左到右扫过去,如果左边的大于右边的,要么左边的减掉使其等于右边的,要么右边的加上使其等于左边的. $dp[i][j]$:前i个数以原序列排序后的第j个数为结尾需要的最少操作. 状态转移方程:$dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b

Codeforces 713C Sonya and Problem Wihtout a Legend(单调DP)

[题目链接] http://codeforces.com/problemset/problem/713/C [题目大意] 给出一个数列,请你经过调整使得其成为严格单调递增的数列,调整就是给某些位置加上或者减去某个数,调整的代价是加上或者减去的数的绝对值之和,请你输出最小代价. [题解] 先考虑这样一个问题,如果是非严格单调递增该如何做,我们会发现每次调整,都是调整某个数字为原先数列中存在的数字,最后才是最优的,所以,我们设DP[i][j]表示前i个数字,最后一个数为原先数列排序后第j大的数字的最

CF713C Sonya and Problem Wihtout a Legend (经典dp)

一个经典的通过增长减小大小来求使得序列单调性的最小代价. 对于这道题,有一个前置题是不要求要严格单调,而是不严格单调 这样的话,我们可以得到一个性质,最后所有的值都是在原序列当中的,这其实用贪心的想法想一想就好,因为一旦通过加减与左边或右边相等,就没必要再加减了 但是本题要求严格,这就很难说了,因此要考虑将一个问题转化成已知的问题 对于原先的问题,其实就是a[i]-a[j]>=0就好 那么现在的问题是a[i]-a[j]>=i-j,因此我们只要对输入的原数列减下标i,就转化成上面的问题了 也就是

把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend

1 //把一个序列转换成严格递增序列的最小花费 CF E - Sonya and Problem Wihtout a Legend 2 //dp[i][j]:把第i个数转成第j小的数,最小花费 3 //此题与poj 3666相似 a[i]转换成a[i]-i 4 5 #include <iostream> 6 #include <cstdio> 7 #include <cstdlib> 8 #include <algorithm> 9 #include <

Sonya and Problem Wihtout a Legend

Sonya was unable to think of a story for this problem, so here comes the formal description. You are given the array containing n positive integers. At one turn you can pick any element and increase or decrease it by 1. The goal is the make the array

CodeForces 713C Sonya and Problem Wihtout a Legend

$dp$. 非严格递增的可以由$dp$解决.可以将严格递增转化为非严格递增. 要保证$a[i]<a[i+1]$,就是要保证$a[i]<=a[i+1]-1$,也就是$a[i]-i≤a[i+1]-1-i$,等价于$a[i]-i≤a[i+1]-(i+1)$. 因此只要将$a[i]-i$,求非严格递增的就可以了. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #inclu

CF713C Sonya and Problem Wihtout a Legend &amp; hihocoder1942 单调序列

这两个题是一样的,不过数据范围不同. 思路1: 在CF713C中,首先考虑使生成序列单调不下降的情况如何求解.因为单调上升的情况可以通过预处理将a[i]减去i转化成单调不下降的情况. 首先,生成的序列中的每个数一定是原序列中的数.于是可以通过dp+离散化技巧解决.dp[i][j]表示把前i个数变成单调不下降的序列,并且a[i]不超过第j大的数所需要的最小代价. 实现1: 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef lo

BZOJ 2298 problem a(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2298 题意:一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低.”问最少有几个人没有说真话(可能有相同的分数) 思路:对于第i个人来说,区间[ai+1,n-bi]的人的分数相同.那么我们用sum[L][R]表示区间[L,R]中总人数.用f[i]表示前i个人中说真话的最大人数,那么f[j]=max(f[i-1]+sum[i][j]). map<pair<in

poj Problem A. Score Sequence(暴力)

这道题,对于我这种英文不好的人来说,有点费劲啊. 题目的意思:给你两组成绩,你要找出他们之间最大的公共子序列,不能有重复,然后输出两组数据. 第一组就是:按照从大到小输出最大子序列. 第二组就是:按照个位数由小到大输出,若个位数一样大,则小的在前输出最大子序列. 解题思路基本上已经出来了,就是千万要注意就是在最长子序列匹配之前就就把重复的数字给删除. 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h