從同的分組,每組“必須”選擇一個,使得總的最小帶寬 與 總費用的比值最大
一開始也沒想到怎麼DP法,但跟分組揹包問題很類似
分組揹包問題:
問題
有N件物品和一個容量爲V的揹包。第i件物品的費用是c[i],價值是w[i]。這些物品被劃分爲若干組,每組中的物品互相沖突,最多選一件。
求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。
f[k][v] = max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i屬於第k組} 第k組一件都不選或者選擇一件
最多選一件和必須選一件是不一樣的
類似的,對於這道題目建立狀態,一開始想到的是 d[i][j] 0 -- i - 1組花費爲j的時候所能獲得的最大帶寬結論是d[n-1][p] / p 的最大值
轉移方程 d[i][j] = max { d[i][s] (s <= j), min{ d[i - 1][j - c[k] ] , b[k] } 即選擇第i組中第k個設備,帶寬爲b[k],花費爲c[k],從d[i-1][j - c[k]]轉移
那麼對於所有的費用需要進行累加,數值會非常大,而且對於組內的狀態d[i][j]更新計算比較複雜
換一種方式 d[i[j]表示第0-i-1組最大帶寬爲j的最小花費
結論是 j / d[i][j]的 最大值
轉移方程爲 d[i][j] = min { d[i][j] ,min { d[i - 1][s] + c[k] } } s >= j 且 k爲第i組第c[k]個物品的花費
維護一個最大的帶寬maxb即可,不需要累加總的費用
poj 1018
#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;
///宏定義
const int INF =500000000;
const int maxn = 1111;
///全局變量 和 函數
//
int T;
int d[maxn][maxn]; //第0-i組,帶寬爲j的最小花費d[i][j]
int b[maxn], p[maxn];
int main()
{
int i, j;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
int m;
int maxb = -1;
for(i = 0; i < n; i++)
{
for(j = 0; j < maxn; j++)
d[i][j] = INF;
scanf("%d", &m);
// maxb = -1;
for(j = 0; j < m; j++)
{
scanf("%d %d", &b[j], &p[j]);
maxb = max(maxb, b[j]);
}
//初始化第一組的數據
if(i == 0)
{
for(j = 0; j < m; j++)
{
d[i][b[j]] = min(d[i][b[j]], p[j]);
}
continue;
}
for(j = 0; j <= maxb; j++)
{
// d[i][j] = INF;
if (d[i - 1][j] == INF)
continue;
for(int k = 0; k < m; k++)
{
int minb = min(j, b[k]);
d[i][minb] = min(d[i][minb], d[i - 1][j] + p[k]);
}
}
}
double ans = -1;
for(i = 0; i <= maxb; i++)
{
if (d[n - 1][i] != INF)
ans = max(ans, double(i) /d[n - 1][i]);
}
printf("%.3f\n", ans);
}
return 0;
}