DP题:定义dp_b[i][j]为考虑第i种物品时,选择第j个物品,这样的方案时,有效带宽。
dp_p[i][j]为考虑第i种物品时,选择第j个物品,这样的方案时,价格总和。
则状态转移方程:
dp_b[i][j] = max{ min{ dp_b[i-1][k],band[i][j] } / ( dp_p[i-1][k] + price[i][j] )} for all k<=K K是第i-1种物品的数量。
复杂度O(n.^3),还好n不大,n<=100.
这道题WA了好几次,最后自己终于找到原因了:在上面的公式中取max的时候,当遇到相等的项时,是否要考虑覆盖原来的选择呢? 答案是:要考虑!当遇到相等的max值时,优先选择带宽大的方案。
改好后,终于AC了。 5A,也还比较满意了。
更新@Aug1,2012:
鉴于有人说我的方法不能AC,我被躺枪说是骗子, 我把代码贴出来吧。
代码在本篇日志的最后。
说明: 本算法确实有缺陷,不过试了,能AC, 原因是POJ这道题目的测试数据太弱了,写测试数据的人,没有考虑到这种情况。
12月份算法学习小结
这个月DP的功力虽然没有像之前的目标一样,竭尽全力的去DP,达到理想的效果。不过,自己在DP上确实有了不小的长进.
总结下:11月份掌握的算法主要涉及:
1.常见的背包问题:01背包,完全背包,多重背包。
2.经典的DP问题:矩阵链乘法,最长公共子串,最长上升子序列(n.^2算法和nlogn算法)
3.一些简单的状态转移方程的DP问题。
下面上图,12月份的代码量统计结果:
1)12月份的ACM代码文件夹示意图:
2)12月份总代码量图:
总共104个CPP文件,7070行代码,其中有372行blank lines(5.26%的空行率)
我的POJ1018 AC代码,刚又重新submit了一遍,确实可以AC。
//poj_1018
#include <iostream>
#include <cassert>
using namespace std;
const int MAX_N = 101;
const int INFINITE = 1<<30;
int N;
int nums[MAX_N];
int** b;
int** p;
int** dp_b;
int** dp_p;
const double ZERO_D = 0.0000001;
double DP()
{
for(int j=1; j<=nums[1]; ++j)
{
dp_b[1][j] = b[1][j];
dp_p[1][j] = p[1][j];
}
for(int i=2; i<=N; ++i) //考虑第i种device
{
for(int j=1; j<=nums[i]; ++j) //考虑第j个物品
{
double max_tmp = -INFINITE;
//double max_tmp = 0;
//if( i == 1 )
//{
// double tmp = 1.0*b[i][j]/p[i][j];
// if( max_tmp < tmp )
// {
// max_tmp = tmp;
// dp_b[i][j] = b[i][j];
// dp_p[i][j] = p[i][j];
// }
// continue;
//}
for(int k=1; k<=nums[i-1];++k)
{
int band = min( dp_b[i-1][k],b[i][j] );
int price = dp_p[i-1][k] + p[i][j];
double bp = 1.0*band/price;
if( max_tmp < bp )
{
max_tmp = bp;
dp_b[i][j] = band;
dp_p[i][j] = price;
}
else if( abs( max_tmp - bp ) <= ZERO_D && band > dp_b[i][j] )
{
max_tmp = bp;
dp_b[i][j] = band;
dp_p[i][j] = price;
}
}
double rub = max_tmp;
}
}
double ret = -INFINITE;
for(int j=1; j<=nums[N]; ++j)
{
double tmp = dp_b[N][j]*1.0/dp_p[N][j];
ret = ret < tmp ? tmp : ret;
}
return ret;
}
int main()
{
//freopen("in.txt","r",stdin);
int testcase;
scanf("%d",&testcase);
while( testcase-- > 0 ){
scanf("%d",&N);
b = new int*[N+1]; //bandwidth
p = new int*[N+1]; //price
dp_b = new int*[N+1];
dp_p = new int*[N+1];
nums[0] = 0;
for(int i=1; i<=N; ++i)
{
scanf("%d",nums+i);
b[i] = new int[nums[i]+1];
p[i] = new int[nums[i]+1];
dp_b[i] = new int[nums[i]+1];
dp_p[i] = new int[nums[i]+1];
//for(int j=0; j<=nums[i]; ++j)
//{
// dp_b[i][j] = INFINITE; //init bandwidth with INFINITE
// dp_p[i][j] = 0;
//}
for(int j=1; j<=nums[i]; ++j)
scanf("%d %d",&b[i][j],&p[i][j]);
}
double res = DP();
printf("%.3f\n",res);
for(int i=1; i<N+1; ++i)
{
delete[] b[i]; b[i] = NULL;
delete[] p[i]; p[i] = NULL;
delete[] dp_b[i]; dp_b[i] = NULL;
delete[] dp_p[i]; dp_p[i] = NULL;
}
delete[] b; b = NULL;
delete[] p; p = NULL;
delete[] dp_b; dp_b = NULL;
delete[] dp_p; dp_p = NULL;
}
return 0;
}