您的位置:首页 > 其它

la 4731 贪心+DP+概率

2016-04-07 21:42 218 查看
其实不要概率知识……题目给了公式

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

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

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

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

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

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

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

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

上愚蠢的代码

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

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<iostream>
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
[w] / sum
);

}
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm dp