hdu 4261 Estimation (优先队列预处理+DP)

Estimation

Time Limit: 40000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 598    Accepted Submission(s): 206


Problem Description
“There are too many numbers here!” your boss bellows. “How am I supposed to make sense of all of this? Pare it down! Estimate!”
You are disappointed. It took a lot of work to generate those numbers. But, you’ll do what your boss asks.
You decide to estimate in the following way: You have an array A of numbers. You will partition it into k contiguous sections, which won’t necessarily be of the same size. Then, you’ll use a single number to estimate an entire section. In other words, for your array A of size n, you want to create another array B of size n, which has k contiguous sections. If i and j are in the same section, then B[i]= B[j]. You want to minimize the error, expressed as the sum of the absolute values of the differences ( Σ| A[i]- B[i]|).
 

Input
There will be several test cases in the input. Each test case will begin with two integers on a line, n (1≤ n≤2,000) and k (1≤ k≤25, kn), where n is the size of the array, and k is the number of contiguous sections to use in estimation. The array A will be on the next n lines, one integer per line. Each integer element of A will be in the range from -10,000 to 10,000, inclusive. The input will end with a line with two 0s.
 

Output
For each test case, output a single integer on its own line, which is the minimum error you can achieve. Output no extra spaces, and do not separate answers with blank lines. All possible inputs yield answers which will fit in a signed 64-bit integer.
 

Sample Input
  
  
7 2 6 5 4 3 2 1 7 0 0
 

Sample Output
  
  
9
 

Source
 

思路来源于:cxlove博客

题意:
给出一个序列A,分为K个部分,然后每个部分给出一个B(自己给出),使得所有的∑(|Ai-Bi|)最小

思路:
要使一个部分的|Ai-Bi|最小,如果个数为奇数,那么所选的Bi一定为中位数,2°若为偶数那么一定是中间两个数中间的一个数,用sum表示大的减小的数,1° 则|Ai-Bi|=sum-中位数,2°则|Ai-Bi|=sum  (可以画个区间的图就能很好的反映出来了)。
首先肯定是预处理w[i][j] - i到j为一段时|Ai-Bi|的最小值,实现时可以用两个优先队列lower、upper,一个存较小的数,一个存较大的数,维护好sum以及lower和upper 的个数就好了。
然后就是dp了,dp[i][j] - 将1到j分i段时最小的值。
那么 dp[i][j]=min(dp[i][j],dp[i-1][k]+w[k+1][j]);

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 70
#define MAXN 31290
#define OO (1<<31)-1
#define mod 100000000
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;

int n,m,ans,cnt,tot,flag;
int a[2005],dp[30][2005],w[2005][2005];

int main()
{
    int i,j,k,t,num1,num2,sum;
    while(scanf("%d%d",&n,&m),n|m)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1;i<=n;i++)
        {
            priority_queue<int>lower;
            priority_queue<int,vector<int>,greater<int> >upper;
            sum=0;
            for(j=i;j<=n;j++)
            {
                if(upper.empty()||a[j]>=upper.top())
                {
                    upper.push(a[j]);
                    sum+=a[j];
                }
                else
                {
                    lower.push(a[j]);
                    sum-=a[j];
                }
                num1=(j-i+1)/2; num2=j-i+1-num1;  // 两个队列应该的大小
                if(lower.size()>num1)
                {
                    upper.push(lower.top());
                    sum+=2*lower.top();
                    lower.pop();
                }
                else if(upper.size()>num2)
                {
                    lower.push(upper.top());
                    sum-=2*upper.top();
                    upper.pop();
                }
                if(num2>num1) w[i][j]=sum-upper.top();
                else w[i][j]=sum;
            }
        }
        for(i=0;i<=m;i++) for(j=0;j<=n;j++) dp[i][j]=INF;
        dp[0][0]=0;
        for(i=1;i<=m;i++)
        {
            for(j=i;j<=n;j++)
            {
                for(k=0;k<j;k++)
                {
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+w[k+1][j]);
                }
            }
        }
        printf("%d\n",dp[m][n]);
    }
    return 0;
}









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值