题目 G : 寻找dijun

题目描述 

小明在玩一个战略游戏。他现在的任务是找到敌方的军队在什么地方。他已经知道敌方的军队可能在的几个区域和每个区域敌方的军队可能存在的概率,且敌方的军队只可能存在于这些区域中的某一个区域当中。他拥有一个科技:可以同时扫描若干个区域并花费区域个数的金钱。但游戏有一定的限制,小明必须将这些区域分成 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

js君

关注我,还有跟多干货

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值