UVA1754 Posterize

算法竞赛 动态规划
编辑文章

题意

有一个长为 $n(\le 256)$ 的数组 $a$ ,第 $i$ 个数有权值 $b_i$ 。从中选取 $K$ 个点 $c_j$ ,把 $a$ 上的数都移过去,代价为 $\sum (a_i-c_j)^2\times b_i$ 。求最小代价。

题解

非常暴力的DP。用 $f[i][j]$ 表示前 $i$ 个数选了 $j$ 个点,其中第 $i$ 个点选的最小代价。预处理选取 $i,j$ 后 $(i,j)$ 区间移动的代价 $v[i][j]$ ,方程式为:

$$f[i][j]=\min f[k][j-1]+v[k][i]$$

然后枚举第一个取的和最后一个取的位置,中间暴力 $O(n^3)$ 转移即可。

#include<bits/stdc++.h>
#define ll long long

using namespace std;

inline ll read()
{
    char ch=getchar(); ll f=1,x=0;
    while (ch<'0' || ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
    while (ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
    return f*x;
}

const ll N=265;
ll n,k,a,s[N],f[N][N],v[N][N];

signed main()
{
    while (~scanf("%lld%lld",&n,&k))
    {
        memset(s,0,sizeof(s));
        for (ll i=1;i<=n;i++) a=read(),s[a+1]=read();
        for (ll i=1;i<=256;i++)
        {
            for (ll j=i+2;j<=256;j++)
            {
                ll mid=(i+j)>>1;
                v[i][j]=0;
                for (ll k=i+1;k<j;k++)
                {
                    if (k>mid) v[i][j]+=(j-k)*(j-k)*s[k];
                    else v[i][j]+=(k-i)*(k-i)*s[k];
                }
            }
        }
        for (ll i=1;i<=256;i++) //第一个取的位置
        {
            f[i][1]=0;
            for (ll j=1;j<i;j++) f[i][1]+=(i-j)*(i-j)*s[j];
        }
        for (ll j=2;j<=k;j++) //暴力转移
        {
            for (ll i=j;i<=256;i++)
            {
                f[i][j]=f[i-1][j-1];
                for (ll k=j-1;k<i-1;k++)
                {
                    f[i][j]=min(f[i][j],f[k][j-1]+v[k][i]);
                }
            }
        }
        ll ans=f[256][k];
        for (ll i=255;i;i--) //最后一个取的位置
        {
            ll cur=0;
            for (ll j=256;j>i;j--) cur+=(i-j)*(i-j)*s[j];
            ans=min(ans,f[i][k]+cur);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

新评论

称呼不能为空
邮箱格式不合法
网站格式不合法
内容不能为空