测绘

测绘

Descrption

  • 为了研究农场的气候,Betsy帮助农夫John做了 (N(1≤N≤100)) 次气压测量并按顺序记录了结果(M_1,M_2,…,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_{S1}|)
      • 如果 (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)) ,找出样本 (k) 最小的一部分结果使得误差最多为 (E).

Input

  • 第一行:两个数 (N,E)(含义如题)。
  • 接下来 (N) 行每行一个数表示气压。

Output

  • 输出一行,两个数,第一表示最小的样本数 (K),第二个表示这 (K) 个样本的最小误差值

Sample Input

4 20
10
3
20
40

Sample Output

2 17

Hint

  • 选择第二次和第四次的测量结果能达到最小误差(17)

  • 分析:

    • 看到这种题型,我们首先要考虑 (dp) ,看能不能找到最优子结构,典型的线性 (dp) 的状态定义为 (dp[i]) 表示前 (i) 个元素作为一个子问题,但选多少个数,这显然是限制条件,很容易定义 (dp[i][j]) ,表示前 (i) 个数选择 (j) 个数的最小误差。那如何转移呢?

    • 首先我们要确定一个问题,就是第一维的前 (i) 是否包含第 (i) 个元素的问题,显然必须包含第 (i) 个元素,且为所选子序列的最后一个元素,即为 (S_k) ,这样我们才好转移。转移的决策就是从 (i) 往后找一个元素加入序列。

    • 转移方程:(dp[i][j]=min(dp[i][j],dp[k][j-1]+?),(j-1le k<i))

      • 首先我们预处理出一个数组(pre)(pre[i][j]) 表示原序列中(M_i,M_j) 是所选子序列的相邻的两个元素,则 (i)(j) 之间元素对误差的贡献,枚举 (k,(i+1le kle j-1)) ,计算(abs(2*M_k-M_i-M_j)) ,显然我们可以 (n^3) 暴力枚举预处理出此结果。
      • (pre[i][0]) 记录如果 (M_i) 作为序列第一个数,则(M_1sim M_{i-1}) 的误差之和,(pre[i][n+1]) 记录如果 (M_i) 作为所选子序列的最后一个元素,则 (M_{i+1}sim M_n) 的误差之和。
      • 所以,(?=-pre[k][n+1]+pre[i][n+1]+pre[k][i])
        • (dp[k][j-1]) 是把 (k) 当成了序列的最后一位,所以(+pre[k][n+1]) ,所以先减去,此时把 (i) 当做了最后一位,所以 (+pre[i][n+1]) ,然后加上 (M_ksim M_i) 之间的误差 (pre[k][i])
    • 转移方程为:(dp[i][j]=min(dp[i][j],dp[k][j-1]-pre[k][n+1]+pre[i][n+1]+pre[k][i] ),(j-1le k<i))

    • 显然是由 选择 (i-1) 个元素的序列推导出选择 (i) 个元素的序列,所以选择了多少个元素作为阶段,为了方便推导,对 (dp[i][j]) 重新定义为:前 (j) 个元素,最后一个元素为 (j) 共选了 (i) 个元素的最小误差。

    • 转移方程:(dp[i][j]=min(dp[i][j],dp[i-1][k]-pre[k][n+1]+pre[i][n+1]+pre[k][i]),(j-1le k<i))

    • 当前行状态只与上一行状态相关,可以用滚动数组压维。

    • Code

      #include <bits/stdc++.h>
      typedef long long LL;
      const int maxn=100+15;
      const int inf=0x3f3f3f3f;
      int n,E;
      LL ans;
      int a[maxn];
      LL dp[maxn][maxn],pre[maxn][maxn];
      void Init(){
      	scanf("%d%d",&n,&E);
          for(int i=1;i<=n;i++)
          	scanf("%d",&a[i]);
          for(int i=1;i<=n;i++){//预处理pre[i][j]
             for(int j=i+1;j<=n;j++)
                 for(int k=i+1;k<=j-1;k++)
                     pre[i][j]+=abs(2*a[k]-a[i]-a[j]);
              for(int j=1;j<i;j++) pre[i][0]+=2*abs(a[j]-a[i]);//i之前
              for(int j=i+1;j<=n;j++) pre[i][n+1]+=2*abs(a[j]-a[i]);//i之后
          }
      }
      void Solve(){
      	int id=n;//记录选的满足条件的最小的个数
      	ans=inf;
          for(int i=1;i<=n;i++){//只选一个数的序列
              dp[1][i]=pre[i][0]+pre[i][n+1];
              if(dp[1][i]<=E){
                  id=1;ans=std::min(ans,dp[1][i]);
              }
          }
          if(id==1){//没有比1小的选择了
              printf("%d %lld
      ",id,ans);return;
          }
          for(int i=2;i<=n;i++){//选i个数
              for(int j=i;j<=n;j++){//第i个数位j    
                  dp[i][j]=inf;
                  for(int k=i-1;k<j;k++){//上一个阶段的最后一个数位k
                      int sum=-pre[k][n+1]+pre[k][j]+pre[j][n+1];
                      dp[i][j]=std::min(dp[i][j],dp[i-1][k]+sum);
                  }
                  if(dp[i][j]<=E){//在误差范围之内
                  	if(i>id) continue;//选的数比已选的大
                      if(i<id){id=i;ans=dp[i][j];}//在保证id尽可能小的情况下再考虑值             
                      if(i==id) ans=std::min(ans,dp[i][j]);
                  }
              }
          }
          printf("%d %lld
      ",id,ans);
      }
      int main(){
          Init();
          Solve();
          return 0;
      }
      
原文地址:https://www.cnblogs.com/hbhszxyb/p/13201521.html