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;
}
开始其实就想到,大的应该放前面,不过还有如何划分等各个问题,所以没有敢直接下手证明
分析了对于某一种划分方式,应该把那个组放在最前面(可证明 (∑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;
}
相关文章推荐
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- 简单的四则运算
- 数的奇偶性
- LFC1.0.0 版本发布
- Android px、dp、sp之间相互转换
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- 矩阵的乘法操作
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置