2049042 | 2006-08-31 00:00:06 | Accepted | 1234 | C++ | 00:00.39 | 448K | St.Crux |
题意:3个数一套,(a1≤a2≤a3),并定义c =(a1 - a2)^2 。在M个已排序数里找出N套这样的数,使得∑c最小。
典型的组合式dp题。这题的关卡在于:数是一套的,而不是一个,而第三个数又是无用的。但是有一点:a1和a2必然是临近的数,(不然必大于这种最优选择)即取到第k个数时,对于ak和ak-1只有两种选择:
1.不取。2.取。(........) 捆绑考虑。
和所有的组合dp一样,可以列出方程:
opt[i, k] = opt[i - 1, k - 2] + (ak - ak-1)^2 (1≤i≤n, 1≤k≤m)
且opt[i, k] = opt[i, k - 1]
其中opt表示前k个数里取i个能取到的最小值。其中k对于每个i都有取值范围,两边都有,这个和组合dp一样,只不过稍显复杂一些。
而这显然要优化,数组开这么大,1000×5000,还好现在内存便宜。但是还得优化,n值一大照样破产。
那么,只需要两个数组便可。这个也是老规矩了,上一步的先存在b[0]里,每个做完了放b[1]里,等k循环完一遍就可把b[1]赋值给b[0]。这是一种经常用的手法,也是所有不写注释的程序最让人百思不得其解的地方。
#include <cstdio>
#include <string>
#define MX 99999999
int a[5001], b[2][5001], c, n, m;
void init()
{
memset(b, 0, sizeof(b));
}
void dp()
{
int i, k;
for(i = 1; i <= m; i ++)
{
for(k = 2 * i; k <= n; k ++)
{
b[1][k] = MX;
if(k > 2 * i) b[1][k] = b[1][k - 1];
if(n - k > (m - i) * 3)
{
int t = b[0][k - 2] + (a[k] - a[k - 1]) * (a[k] - a[k - 1]);
if(t < b[1][k]) b[1][k] = t;
}
}
memcpy(b[0], b[1], sizeof(b[1]));
}
}
void prt()
{
//pt();
printf("%d\n", b[1][n]);
}
int main()
{
//freopen("in.txt", "r", stdin);
scanf("%d", &c);
int i, k;
for(i = 0; i < c; i ++)
{
scanf("%d %d", &m, &n);
m += 8;
for(k = 1; k <= n; k ++)
{
scanf("%d", &a[k]);
}
init();
dp();
prt();
}
return 0;
}
评论