la 4731 贪心+DP+概率

其实不要概率知识……题目给了公式

开始其实就想到,大的应该放前面,不过还有如何划分等各个问题,所以没有敢直接下手证明

分析了对于某一种划分方式,应该把那个组放在最前面(可证明 (∑P)/个数)最大的应该放在前面,这样可以使总值最小

可是还是不知道怎么划分啊,划分的方法太多了,不适合做DP的条件

假设每个算式中前面的系数已经给定(每组的个数,组的顺序已经给定) 那么怎么把各个区域填进去,能达到最大?发现了把大的往前面填能最大(可证明)

所以本质上是求按从大到小排序后的序列,插w-1个板子进行划分,能达到的最大值

借助这个,就可以dp了……愚蠢的我居然用了三维dp,分别待变前i大的区域,划分成j组,最后一组有k个(后来百度发现二维就可以,不过思路是一样的,慢了100倍而已哈哈)

上代码……其实分析清楚问题的某些特点,找到本质,就可以很容易的dp了

另外要注意,公式中前面和后面是分开的,可以找到前面再找后面,这是最优子结构的特征之一

上愚蠢的代码

//dp的初始化一直觉得有点问题,要注意到一些值为不可能的状态
//用记忆化搜索写是不是会理解更好一点?
 



#include
#include
#include
#include
#include
using namespace std;
const int maxn = 101;
const int INF = 111111111;
int dp[maxn][maxn][maxn];//不用设最后一个变量……直接可以完成转移 
int minn[maxn][maxn];
int p[maxn];

bool compare(int x,int y)
{
	return (x > y);
}

int main()
{
    int T;
	scanf("%d",&T);
	while(T--){
		int n,w;
		int sum[maxn];
		scanf("%d %d",&n,&w);
		for(int i = 1; i <= n; i++)
		    scanf("%d",&p[i]);
		for(int j = 0; j < maxn; j++)
		for(int k = 0; k < maxn; k++)
			dp[0][j][k] = INF;
		dp[0][0][0] = 1;
	    for(int j = 0; j < maxn; j++)  minn[0][j] = INF;
	    minn[0][0] = 0;	
		sort(p + 1, p + 1 + n,compare);
		sum[0] = 0;
		//for(int i = 1; i <= n; i++) printf("%d ",p[i]);
		//printf("\n");
		for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + p[i];
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= w; j++){
		    minn[i][j] = INF;
		for(int k = 1; k <= n; k++){
			if (j == 1 & k != i) dp[i][j][k] = INF;//有一些不可能出现的情况要注意 
			else if (k == 1){
			    dp[i][j][k] = minn[i - 1][j - 1] + i*p[i];//公式不要写错 
		    }
			else {
			    if (i < k) dp[i][j][k] = INF;	
			    else dp[i][j][k] = minn[i - k][j - 1] + i*(sum[i] - sum[i - k]);
		    }
			 minn[i][j] = min(minn[i][j],dp[i][j][k]); 
	       // printf("%d %d %d %d\n",i,j,k,dp[i][j][k]);
		}
	    }
	    printf("%.4lf\n",1.0 * minn[n][w] / sum[n]);
	    
    }
    return 0;
}
	    
		 




 

你可能感兴趣的:(acm,acm,dp)