JZOJ2018提高组-测绘

测绘

题目大意

为了研究农场的气候, \(Betsy\) 帮助农夫 \(John\) 做了 \(N(1 <= N <= 100)\) 次气压测量并按顺序记录了结果 \(M_1...M_N(1 <= M_i <= 1,000,000)\) , \(Betsy\) 想找出一部分测量结果来总结整天的气压分布. 她想用 \(K(1 <= K <= N)\) 个数 \(s_j(1 <= s_1 < s_2 < ... < s_K <= N)\) 来概括所有测量结果. 她想限制如下的误差:
 对于任何测量结果子集,每一个非此子集中的结果都会产生误差.总误差是所有测量结果的误差之和.更明确第说, 对于每一个和所有 \(s_j\) 都不同的 \(i\) :
  * 如果 \(i\) 小于 \(s_1\), 误差是: \(2 * | M_i - M_(s_1) |\)
  * 如果 \(i\)\(s_j\)\(s_(j+1)\) 之间,误差是: \(| 2 * M_i - Sum(s_j, s_(j+1)) |\)
  注: \(Sum(x, y) = M_x + M_y\) ; ( \(M_x\)\(M_y\) 之和)
  * 如果 \(i\) 大于 \(s_K\) ,误差为: \(2 * | M_i - M_(s_K) |\)
  \(Besty\) 给了最大允许的误差 \(E (1 <= E <= 1,000,000)\) ,找出最小的一部分结果使得误
差最多为 \(E\).

Input

  第一行: 两个空格分离的数: \(N\)\(E\)
  第 \(2..N+1\) 行: 第 \(i+1\) 行包含一次测量记录: \(M_i\)

Output

  第一行: 两个空格分开的数: 最少能达到误差小于等于 \(E\) 的测量数目和使用那个测量数目能达到的最小误差.

样例

输入

4 20
10
3
20
40

输出

2 17

思路

首先我们要理解一点就是这道题是数量优先,能选最少的就选最少的,在数量最少的情况下再算误差的最小,理解了这个再往下看,我一开始并没有想到是个 \(dp\) 的题目,专心研究怎么暴力(啊这),但是后来仔细一想,这真的就是一个赤裸裸的线性 \(dp\),首先我们定义dp数组的含义, \(dp[i][j]\)表示的含义就是在前j个数里选i个测试点的最小误差,那么再思考一个问题,\(dp[i][j]\) 这个状态一定选了第 \(j\) 个节点作为测试点了(你品,你细品),否则这道题的转移方程就很难写...,转移方程还要注意一个方面,不能只算前j个数的误差,一定是把j之后的误差都要算上,在这里就不举例子了,转移方程:\(dp[i][j]=min(dp[i][j],dp[i-1][k]-p[k][N+1]+p[k][j]+p[j][N+1]\),其中p数组是已经预处理好之后的状态,讲到这里就没什么好说的了,首先先初始化,然后 \(dp\) 处理,具体看代码。

代码实现

#include
using namespace std;
const int INF = 0x3f3f3f3f;
long long N,E;
long long ans,dp[120][120],p[120][120];
long long a[120];
void Read_pri(){//初始化+输入数据(没写快读)
	scanf("%lld%lld", &N, &E);
	for(int i = 1; i <= N; i++){
		scanf("%lld",&a[i]);
	}
	for(int i = 1; i <= N; i++){
		for(int j = i + 1; j <= N; j++){
			for(int k = i + 1; k < j; k++){
				p[i][j] += abs(2 * a[k] - a[i] - a[j]);
			}
		}
		for(int j = 1; j < i; j++){
			p[i][0] += 2*abs(a[i]-a[j]);
		}
		for(int j = i + 1; j <= N; j++){
			p[i][N+1] += 2*abs(a[j] - a[i]);
		}
	}
}

int main(){
	Read_pri();
	int id = N;
	ans = INF;
	for(int i = 1; i <= N; i++){//特判一下一个测试点是否可以满足条件
		dp[1][i] = p[i][0] + p[i][N+1];
		if(dp[1][i] <= E){
			id = 1;
			ans = min(ans, dp[1][i]);
		}
	}
	if(id == 1){
		printf("%d %lld",id, ans);
		return 0;
	}
	for(int i = 2; i <= N; i++){
		for(int j = i; j <= N; j++){
			dp[i][j] = INF;
			for(int k = i - 1;k < j; k++){
				dp[i][j] = min(dp[i][j],dp[i - 1][k] - p[k][N + 1] + p[k][j] + p[j][N+1]);
			}
			if(dp[i][j] <= E){
				if(i > id) continue;
				else if(i < id){
					id = i;
					ans = dp[i][j];
				}else{
					ans = min(ans, dp[i][j]);
				}
			}
		}
	}
	printf("%d %lld",id, ans);
	return 0;
}

谢谢阅读

你可能感兴趣的:(JZOJ2018提高组-测绘)