bnu 51640 Training Plan(类似区间dp)(北师16校赛)

5 篇文章 0 订阅



小Q同学为了准备今年的ICPC Regional,计划在m天之内刷掉n道题,每道题有一个难度值,其中第i道题的难度值为a[i]

然而处于半颓废状态中的小Q同学不希望在同一天中做难度差距悬殊的题目,定义第i天中刷的题的难度的最大值减最小值为d[i](如果第i天没有刷题,则d[i]=0),那么整个计划的难度为\sum_{i=1}^{m}{d^2[i]}

小Q同学可以按照任意的顺序刷题,并且一天中可以刷任意多道题,但是每道题只需要做一次,现在小Q同学想知道完成这个计划的总难度的最小值是多少。

Input

第一行是一个正整数T(\leq 10),表示测试数据的组数,

对于每组测试数据,

第一行是两个整数n(1\leq n \leq 500)m(1\leq m \leq 500),表示题数和天数,

第二行是n个整数a[i](0\leq a[i]\leq 1000000),表示每道题的难度值。

Output

对于每组测试数据,输出一个整数,表示整个计划的最小难度。

Sample Input

2
3 3
1 2 3
3 2
1 2 3

Sample Output

0
1

Hint

对于第一组样例,最优方案是一天刷一题。

对于第二组样例,一个最优方案是第一天刷难度值为1和2的题,第二天刷难度值为3的题。

题目大意:

                n天刷m道题,m道题可以随意分配,每天的难度值是当天题目的最大最小难度之差的平方,求所有天加一起的难度最小值

解题思路:

               将这些题的难度从小到大排好序(从大到小也可以),可以断定每天做的题一定是这里连续的区间,因为假设跳了n个,那么交换这个和当前区间的难度最大的那几个题,和下一天交换,答案一定更小。

               之后用dp[i][j]表示第i天刷到第j题的最小值,那么dp[i][j]=min(dp[i-1][k]+(a[j]-a[k])^2)(k=1...j-1),dp[n][m]即为所求。

注意:INF要开的足够大,否则会wa

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#define C(a) memset(a,0,sizeof a)
#define C_1(a) memset(a,-1,sizeof a)
#define C_I(a) memset(a,0x3f,sizeof a)
using namespace std;
typedef long long ll;
const ll INF = 1000000000000000000ll;
const int maxn = 1e6 + 20;
int t[maxn];
ll a[maxn];
int ca;
ll dp[520][520];
int n, m;
int main()
{
	int T; cin >> T;
	while (T--)
	{
		C(t);
		C(dp);
		C(a);
		ca = 0;
		scanf("%d%d", &n, &m);
		int x;
		for (int i = 0; i<n; i++)
		{
			scanf("%d", &x);
			if (t[x] == 0)a[ca++] = x;
			t[x]++;
		}
		sort(a, a + ca);
		if (ca <= m) { printf("0\n"); continue; }
		for (int j = 0; j<ca; j++)dp[1][j] = (a[j] - a[0])*(a[j] - a[0]);
		for (int i = 2; i <= m; i++)
		{
			dp[i][0] = 0;
			for (int j = 1; j<ca; j++)
			{
				dp[i][j] = INF;
				for (int k = 0; k<j; k++)
					dp[i][j] = min(dp[i][j], dp[i - 1][k] + (a[j] - a[k + 1])*(a[j] - a[k + 1]));
			}
		}
		cout << dp[m][ca - 1] << endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值