8597 石子划分问题
时间限制:500MS 内存限制:1000K
提交次数:155 通过次数:53
题型: 编程题 语言: G++;GCC;VC
Description
给定n个石子,其重量分别为a1,a2,a3,...,an。 要求将其划分为m份,每一份的划分费用定义为这份石子中最大重量与最小重量差的平方。 总划分费用为m份划分费用之和。现在对于给定的n个石子,求一种划分方案,使得总划分费用最小。
输入格式
第一行两个正整数n和m,接下来一行有n个正整数,表示一个石子的重量ai。(1≤n, m, ai≤1000)
输出格式
计算输出最小总划分费用。注意:若一份只有一个石子,那么,这份石子中最大重量与最小重量的差的平方为0。
输入样例
4 2 4 7 10 1
输出样例
18
提示
1,先将石子重量从小到大排序(从大到小也可以). 2,假设f(n,m)表示:n个石头分成m份的最小费用. 特别的,有f(1,1)=0; f(n,1)=(an - a1)^2 那么,除去最后一份石头的若干个,前面m-1份必定也是最优的分法. 若最后一堆1个石头, f(n,m) = f(n-1,m-1)+0^2 若最后一堆2个石头, f(n,m) = f(n-2,m-1)+(an - an-1)^2 若最后一堆3个石头, f(n,m) = f(n-3,m-1)+(an - an-2)^2 ...... 最后一堆最多只能有n-m+1个石头,因为当最后一堆为n-m+1时,前面m-1堆已经是一个一份了. 因此, f(n,m) = Min{ f(n-1,m-1)+0^2, f(n-2,m-1)+(an - an-1)^2, ...}例如: n=5, m=2 a[1..5] = 1 3 4 8 9 f(5,2)=Min{ f(4,1)+0; f(3,1)+1; f(2,1)+5^2 }=Min{49,10,29}=10 这5个石头分2堆的最优分法:(1 3 4)(8 9)
这题需要记录,做的时候居然又忘记了。
首先贪心,排序后,相邻的排在一堆是必然的。
然后每次只考虑最后一堆即可是吧,其他的递归处理。
所以,设dp[i][j]表示前i个数字,分成j堆的最小值。
那么,考虑最后一堆,肯定是min(dp[1...i-1][j-1] + pow(a[n] - a[n - i + 1], 2));
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL;#include <iostream> #include <sstream> const int maxn = 1e3 + 20; int dp[maxn][maxn]; int a[maxn]; int dfs(int n, int m) {if (m == 1) return (a[n] - a[1]) * (a[n] - a[1]);if (n == m) return 0;if (dp[n][m]) return dp[n][m];int ans = 1e9;for (int i = 1; i <= n - m + 1; ++i) {ans = min(ans, dfs(n - i, m - 1) + (a[n] - a[n - i + 1]) * (a[n] - a[n - i + 1]));}return dp[n][m] = ans; }void work() {int n, m;cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> a[i];sort(a + 1, a + 1 + n);cout << dfs(n, m) << endl; }int main() { #ifdef localfreopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endifwork();return 0; }
作者
zhengchan