动态规划(求中位数与概率问题)
问题:
1、设A和B都是从小到大已经排好序的n个不等的整教构成的数组, 如果把A和B合并后的数组记作C,设计一个算法找出c的中位教。
2、2、一个实验可得出n个不同的值分别用数xx,x表示已知x,出现的概率是p,,i=2…n,且所有概率之和等于10试设计一个算法找到值x;,使得所有小于x,的值出现的概率之和不超过1/2,且所有大于x的值出现的概率之和也不超过1/2。例如,实验结果为: x=2x=lx=4x.=3.x,=s,出现的概率依次是: p=0.2,D2 =04,p;=0.1,p.=01,p; =02,那么x就是所求的值。比x小的数只有xz ,它的概率是0.45比x的大的数有x,,x,x,它们的概率之和是0.4。
实现代码:
- 动态规划算法求两组数中位数
// 两个长度不相等的有序数组寻找中位数
#include <stdio.h>
#include <iostream>
using namespace std;
double Find_Media(int a[] , int lengtha , int b[] , int lengthb)
{
int mida = lengtha/2;
int midb = lengthb/2;
int l = (mida <= midb) ? mida : midb; // 数组不一样长的时候,保证两边去掉的长度一致
if(lengtha == 1)
{
if(lengthb % 2 == 0) // 数组a与b中位数位置决定了最后的结果
{
if(a[0] >= b[midb])
return b[midb];
else if(a[0] <= b[midb-1])
return b[midb-1];
else return a[0];
}
else return (double)(a[0]+b[midb])/2;
}
else if(lengthb == 1)
{
// 对测试情况2的处理
if(lengtha % 2 == 0)
{
if(b[0] >= a[mida])
return a[mida];
else if(b[0] <= a[mida-1])
return a[mida-1];
else return b[0];
}
else
return (double)(b[0]+a[mida])/2;
}
if(a[mida] == b[midb]) { // 两个数组的中位数相等则找到
/*避免类似2323的情况出现*/
if (lengtha %2==0 && lengthb%2 ==0){
if (a[mida-1]<b[midb-1]){
return (double)(b[midb-1]+a[midb])/2;
}else return (double)(a[mida-1]+b[midb])/2;
}else
return a[mida];
}
else if(a[mida] < b[midb]) // 不等则递归,直至某一方的数组长度变为1
return Find_Media(&a[mida] , lengtha-l , &b[0] , lengthb-l);
else
return Find_Media(&a[0] , lengtha-l , &b[midb] , lengthb-l);
}
// 给出三组测试用例,分别为三种情况
int main(){
// test1:两个数组长度一致都为偶数
int a[] = {1,2,3,4,5};
int b[] = {4,6,7,8,9};
int lena = sizeof(a)/sizeof(a[0]);
int lenb = sizeof(b)/sizeof(b[0]);
cout<<Find_Media(a, lena, b, lenb)<<endl;
cout<<"---------------------------"<<endl;
// test2:按照分组最后的中位数为3,但是实际为2.5,所以代码中加了最此类情况的处理
int c[] = {1,2,3,4};
int d[] = {1,2,3,4};
int lenc = sizeof(c)/sizeof(c[0]);
int lend = sizeof(d)/sizeof(d[0]);
cout<<Find_Media(c, lenc, d, lend)<<endl;
cout<<"---------------------------"<<endl;
test3:两个数组长度不一致
int e[] = {1,3,5,7,9};
int f[] = {2,4,5,8};
int lene = sizeof(e)/sizeof(e[0]);
int lenf = sizeof(f)/sizeof(f[0]);
cout<<Find_Media(e, lene, f, lenf)<<endl;
}
执行结果:

问题2:求一个数使得左右两边的概率都小于0.5
采取的算法是寻找第K小的思想,函数多加入了两个参数K1与K2用于记录每一次查找后的左右两边的概率和,避免重复计算,提高效率。
// 求一组数中的目标值,满足左右两边的概率和小于0.5
#include<iostream>
#include<map>
using namespace std;
map<int, double> p; // 按照键值对存储数据
void findK(int a[], int low ,int high, double k1, double k2){
int i = low, j = high; //把待排序数组元素的第一个和最后一个下标分别赋值给i,j,使用i,j进行排序;
int tmp = a[low]; //将待排序数组的第一个元素作为哨兵
double suml=0,sumr=0; // 记录左右两边的概率和
if(low < high){
while(i<j){
while(i<j && a[j] >= tmp){ // 从右侧开始判断累计概率
sumr+=p[a[j]];
j--;
}
if(i<j) a[i] = a[j]; // 交换不满足条件的
while(i<j && a[i] <= tmp){ //换成左侧下标为i的元素开始
suml+=p[a[i]];
i++;
}
if(i<j) a[j] = a[i]; //交换不符合条件的
}
a[i] = tmp; //完成一次排序,把哨兵赋值到下标为i的位置,即前面的都比它小,后面的都比它大
if(suml+k1< 0.5 & sumr+k2<0.5){ // 满足条件
cout<<a[i]<<endl;
cout<<"左边概率和: "<<suml+k1<<"\n"<<"右边概率和: "<<sumr+k2<<endl;
}
else if(suml+k1>0.5){ // 左边的概率和大
k2+=sumr+p[a[i]]; // 保存右边的概率和,每次递归都大则累加
k1=0; // 左边为0 重新求取
findK(a,low,i-1,k1,k2);
}
else if(sumr+k2>0.5){ // 右边概率和大
k1+=suml+p[a[i]]; // 保存整个左边的概率和,递归一只大则一直加
k2=0; // 此时右边重新求取
findK(a,i+1,high,k1,k2);
}else cout<<"概率分布有误"<<endl;
}
}
int main()
{
int a[] = {2,1,6,9,3};
int len = sizeof(a)/sizeof(a[0]);
double b[] = {0.2,0.2,0.1,0.4,0.1};
for (int i=0;i<len;i++){
p[a[i]] = b[i];
}
findK(a,0,len-1,0,0);
cout<<"-----------------"<<endl;
int c[] = {2,1,3,6,9};
double d[] = {0.2,0.1,0.1,0.4,0.2};
int lenl = sizeof(c)/sizeof(c[0]);
for(int j=0;j<lenl;j++){
p[c[j]] = d[j];
}
findK(c,0,lenl-1,0,0);
}
执行结果:


被折叠的 条评论
为什么被折叠?



