题目描述
小明在玩一个战略游戏。他现在的任务是找到敌方的军队在什么地方。他已经知道敌方的军队可能在的几个区域和每个区域敌方的军队可能存在的概率,且敌方的军队只可能存在于这些区域中的某一个区域当中。他拥有一个科技:可以同时扫描若干个区域并花费区域个数的金钱。但游戏有一定的限制,小明必须将这些区域分成 k 组,且只能对 k 组的区域依次进行一次扫描。现在小明想知道怎么样的划分区域的策略可以使得找到敌军时花费的金钱数的数学期望最小。
输入
输入第一行是一个正整数 T(T<=10),表示数据组数。
接下来对于每组数据,第一行给出两个正整数 n(1<=n<=100)和k(1<=k<=n<=100),分别表示区域个数和划分组数。第二行给出 n 个不超过 100 的正整数,依次代表每个区域的概率(单位:%),其中保证所有区域的概率相加等于 100%。
输出
对于每组数据,输出一个实数,表示花费金钱数的数学期望的最小值,保留 3 位小数。
样例输入
2
5 2
30 5 10 30 25
5 5
30 5 10 30 25
样例输出
3.200
2.300
解题思路
为了让总期望值最小,应该让概率大的区域尽量放在前面去访问。所以先把所有概率从大到小排序一遍。然后分组时,就可以取连续的一段分为一组了。
dp [ i ][ j ]表示: 前i个,分成j组的最小期望则有动态方程:
dp[ i ][ j ] = min {dp[ i ][ j ], dp[ k ][ j - 1] + i * sum[ i~k] k∈[0,i] }
参考代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAX_LEN 105
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
/**
*排序 从大到小
*/
bool cmp(double a,double b)
{
return a > b;
}
int main ()
{
int t;
cin>>t;
while (t--)
{
int n,w;
cin>>n>>w;
double p[MAX_LEN];//概率
double sum[MAX_LEN];//sum[i]p[1]-p[i]的和
double dp[MAX_LEN][MAX_LEN];
for (int i = 1; i <= n; i++)
{
cin>>p[i];
}
sort(p+1, p+n+1, cmp);
sum[0] = 0;
for (int i = 1; i <= n; i++)
{
sum[i] = sum[i-1] + p[i] / 100.0;
}
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= w; j++)
{
dp[i][j] = INF;
}
}
dp[0][0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i && j <= w; j++)//可分为j组
{
dp[i][j] = INF;
for (int k = 0 ; k <= i; k++)
{
dp[i][j] = min(dp[i][j], dp[k][j-1] + (sum[i] - sum[k]) * i );
}
}
}
printf("%.3f\n", dp[n][w]);
}
return 0;
}