前言
考试时题目都没看懂,题目十分玄学
举个栗子
Sum(sj,s(j+1))Sum(sj,s(j+1))就是Sum(msj,ms(j+1))Sum(msj,ms(j+1))
and
用1∼n1∼n的数概况1∼10000001∼1000000的数
反正就是十分的玄学
正题
大意
在nn个数里选kk个数。
如果我们选择个数那么就有一个序列s=s1,s2...sks=s1,s2...sk表示选择第sisi个。
然后对于每一个没有被选择的数都会对结果造成一点误差。
如果没有被选择的数编号为ii,对于每一个
如果 i<s1i<s1, 误差是:2∗|Mi−Ms1|2∗|Mi−Ms1|
如果sj<i<sj+1sj<i<sj+1,误差是:|2∗Mi−msj−msj+1||2∗Mi−msj−msj+1|
如果i>ski>sk,误差为:2∗|Mi−Msk|2∗|Mi−Msk|
求选择最少的数使误差小于ee
解题思路
先预处理一个表示在i∼ji∼j这个区间内选择一个数的最小误差。
然后用一个f[i][j]f[i][j]表示选择第ii个数,前面的已经取了个数时的情况。
然后每次枚举一个在ii前面的从f[i][j]f[i][j]转移到f[k][j+1]f[k][j+1],动态转移方程
f[k][j+1]=max{f[i][j]+g[i+1][k−1]}f[k][j+1]=max{f[i][j]+g[i+1][k−1]}
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
long long n,e,m[105],g[105][105],f[105][105];
int main()
{memset(f,127/3,sizeof(f));scanf("%lld%lld",&n,&e);for (ll i=1;i<=n;i++)scanf("%lld",&m[i]);for (ll i=0;i<=n+1;i++)for (ll j=i+1;j<=n+1;j++)for (ll k=i+1;k<=j-1;k++)if (!i) g[i+1][j-1]+=2*abs(m[k]-m[j]);//特判——左else if (j==n+1) g[i+1][j-1]+=2*abs(m[k]-m[i]);//特判——右else g[i+1][j-1]+=abs(2*m[k]-m[i]-m[j]);//预处理f[0][0]=0;for (ll i=0;i<=n;i++)for (ll j=0;j<=n;j++)if (f[i][j]<=e)for (ll k=i+1;k<=n+1;k++)f[k][j+1]=min(f[k][j+1],f[i][j]+g[i+1][k-1]);//动态转移for (ll i=2;i<=n+1;i++)if (f[n+1][i]<=e)//枚举答案{printf("%lld %lld",i-1,f[n+1][i]);return 0;}
}