Hdu 3480 Division (DP_斜率优化|四边形不等式优化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480


题目大意:给定一个大小为n的集合,要求将集合分成m个子集合,每个集合都有权值,权值为最大值减最小值的平方。m<=5000,n<=10000.


解题思路:还算简单的优化DP吧,因为一眼就能看出是DP而且需要优化。

     要将集合分成m个子集合,并且权值和最小,容易想到对集合元素进行排序,能排序的前提是排序完再划分集合,那么此时划分的集合权值总和将和没排序时的相比相等或者更小,并且使得计算更加容易。比如a<b<c<d,那么(d-a)^2 + (c-b)^2 > (b-a)^2 + (d-c)^2. 

    对于排序完的集合, 状态转移方程为:dp[i][j+1] = Min {dp[k][j] + (arr[i] - arr[k+1])^2}(k < i),朴素的做法复杂度是O(n*m*n),太可怕了。然后呢,就想到斜率优化或者四边形不等式优化了。

    这题既能用斜率优化也能用四边形不等式优化,而斜率优化比四边形不等式优化常数更小,速度更快,本文章着重讲斜率优化,据lasten大神说斜率优化能过的题目四边形不等式都能过,不知可信不可信。

    dp[i][j+1] = min{dp[k][j] + (arr[i] - arr[k+1]) ^2} = dp[k][j] + arr[i]^2 + arr[k+1]^2 - 2 * arr[i] * arr[k+1].

    设y = dp[k][j] + arr[k+1]^2,x = arr[k+1],,那么dp[i][j] = y - 2 * arr[i] * x,转化成这样就可以开始套模版了。

    这题我写了很多遍,一拿到题目用四边形就ac了。然后开始用斜率优化写,之前都是写dp[i][j+1] = dp[k][j] + sum[i] + sum[k] - 2 * sum[i][k],这种和只k有关的DP,用别人的模版倒挺顺的,现在换成了和K+1有关就不知道该怎么搞了。晕了好久,直到今天做网络热身赛遇到差不多的题目才想清楚。其实本质是一样的,写法也是一样,但要先处理j==1的的情况。因为这种时候dp[i][1] = (arr[i] - arr[1])^2都是从0转移过来的,可以不跑单调队列。


测试数据:

Input:
10
3 1
1 2 3
3 2
1 2 2
3 2
1 2 4
4 2
4 7 10 1

OutPut:
Case 1: 4
Case 2: 0
Case 3: 1
Case 4: 18


C艹代码:

//斜率优化DP
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MIN 5010
#define MAX 10010
#define INF 2147483647;


struct point{

    int x,y;
}pot[MAX];
int dp[MAX][MIN];
int qu[MAX],head,tail;
int n, m, arr[MAX];


int CheckIt(int x,int y,int z) {

    point p0 = pot[x],p1 = pot[y],p2 = pot[z];
    return (p0.x - p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x) <= 0;
}
int NotBest(int x,int y,int k) {

    point p0 = pot[x],p1 = pot[y];
    return p0.y - p0.x * k > p1.y - p1.x * k;
}
int Solve_DP() {

    int i, j, k, mmin;


    for (i = 1; i <= n; ++i)
        dp[i][1] = (arr[i] - arr[1]) * (arr[i] - arr[1]);
    for (j = 2; j <= m; ++j) {

        head = tail = 0;
        for (i = j; i <= n; ++i) {

            pot[i].x = arr[i];
            pot[i].y = dp[i-1][j-1] + arr[i] * arr[i];
            while (head <= tail - 1 &&
                    CheckIt(qu[tail-1],qu[tail],i)) tail--;
            qu[++tail] = i;


            while (head + 1 <= tail &&
                    NotBest(qu[head],qu[head+1],2*arr[i])) head++;
            k = qu[head];
            dp[i][j] = pot[k].y - 2 * pot[k].x * arr[i] + arr[i] * arr[i];
        }
    }
    return dp[n][m];
}


int main()
{
    int i, j, k, t, cas = 0;


    scanf("%d", &t);
    while (t--) {

        scanf("%d%d", &n, &m);
        for (i = 1; i <= n; ++i)
            scanf("%d", &arr[i]);
        
        
        sort(arr + 1, arr + 1 + n);
        int ans = Solve_DP();
        printf("Case %d: %d\n", ++cas, ans);
    }
}
//四边形不等式优化
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define MAX 5010
#define INF 2147483647;


int dp[MAX * 2][MAX];
int n, m, arr[MAX * 2], s[MAX * 2][MAX];

int Solve() {

    int i, j, k, mmin;


    for (j = 1; j <= m; ++j) s[n + 1][j] = n;
    for (i = 1; i <= n; ++i)
        dp[i][1] = (arr[i] - arr[1]) * (arr[i] - arr[1]),s[i][1] = 0;
    for (j = 2; j <= m; ++j)
        for (i = n; i >= 1; --i) {

            int mmin = INF;
            for (k = s[i][j - 1]; k <= s[i + 1][j] && k < i; ++k)
                if (dp[k][j - 1] + (arr[i] - arr[k + 1]) * (arr[i] - arr[k + 1]) < mmin)
                    mmin = dp[k][j - 1] + (arr[i] - arr[k + 1]) * (arr[i] - arr[k + 1]), s[i][j] = k;
            dp[i][j] = mmin;
        }


   return dp[n][m];
}


int main() {
    int i, j, k, t, cas = 0;


    scanf("%d", &t);
    while (t--) {

        scanf("%d%d", &n, &m);
        for (i = 1; i <= n; ++i)
            scanf("%d", &arr[i]);
        sort(arr + 1, arr + 1 + n);


        int ans = Solve();
        printf("Case %d: %d\n", ++cas, ans);
    }
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

你可能感兴趣的:(优化,struct,网络,测试,input,output)