首页 > 代码库 > NOJ 1116 哈罗哈的大披萨 【淡蓝】 状态压缩DP
NOJ 1116 哈罗哈的大披萨 【淡蓝】 状态压缩DP
哈罗哈的大披萨 【淡蓝】
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 99 测试通过 : 14
比赛描述
热风哈罗哈(三牌楼)店正在搞活动:他们将提供一个大披萨给第一个告诉他们以下信息的人:一次购买任一种披萨,哪种单位面积的价格最低。问题初步想一想,好像是这么解决:“对于每个披萨计算平均价格,那么最小值就是答案了。”
但是,这个问题不是这么简单,因为在热风哈罗哈店里:对某些披萨,是送打折优惠券的,凭这些优惠券可以得到另一个便宜些甚至差点的披萨,这些优惠券可合并使用。披萨必须一个接一个的买,不可能使用优惠券对一个已买的披萨打折。你能第一个解决这个问题,得到大披萨吗?
输入
输入文件包含几个测试用例。每个测试用例包括:
l 1行:一个数m,为热风哈罗哈提供披萨数目。当m=0时输入终止。一般1 ≤ m ≤ 15。
l 随后的m 行描述每个披萨。每一行描述披萨i (1 ≤ i ≤ m) ,开始3个整数pi, ai 和 ni 表示披萨的价格、面积、和购买它时得到的打折优惠券数目,1 ≤ pi ≤ 10000, 1 ≤ ai ≤ 10000 , 0 ≤ ni < m。
l 随后是ni 对整数xi,j 和 yi,j 表示你得到打折的披萨序号xi,j (1 ≤ xi,j ≤ m, xi,j ≠ i) 以及买披萨xi,j得到的折扣yi,j,以百分比表示(1 ≤ yi,j ≤ 50)。可以假设对于每个i ,xi,j 两两不同。
输出
对于每个测试用例:
打印一行,为通过一次购买任何一种披萨所能得到的单位面积最低价钱. 四舍五入到小数点后4位。
注意到你能组合任意数目的打折优惠券:对于价格为10的披萨和两个折扣为50和20的优惠券,你将只需支付10 * 0.8 * 0.5 = 4单位的钱。
样例输入
1
80 30 0
2
200 100 1 2 50
200 100 0
5
100 100 2 3 50 2 50
100 100 1 4 50
100 100 1 2 40
600 600 1 5 10
1000 10 1 1 50
0
样例输出
2.6667
1.5000
0.5333
题目来源
“IBM南邮杯”团队赛2009
题目意思说的不是很清楚。可以买好多个披萨,但是必须一个一个买,买了一个披萨之后会得到相应的优惠券,然后可以用优惠卷买剩下的披萨。要求计算总的单位面积的披萨的最小值。
至于可以可以买重复的披萨,其实是无所谓的,因为优惠券不能对已经买的披萨打折。如果要第二次购买的披萨的单位面积价格比当前的平均价格高,买了之后会让平均价格变高,得不偿失,如果要购买的披萨比当前的平均价格低,那么一开始的时候只要购买这个不打折的披萨就好了。综上,不需要重复购买披萨。
下面就当做每个披萨只能购买一次来考虑。
使用状态压缩的办法,描述每种购买状态下将要购买某个披萨的折扣,以及初始化每个状态的总的披萨的面积,dp维护每种状态下,购买这些披萨的最小价钱。
算法时间复杂度:O(n22n)
修改优化的地方:
- 使用bit[N]数组来标记二进制标记位对应的披萨编号。
- 使用数组下标来标记折扣代表的披萨号。
- 使用dp的方法计算折扣。
最终AC代码
#include <iostream> #include <stdio.h> #include <vector> #include <cstring> using namespace std; int m, n, x; double p, a, y; double dp[1<<16 | 1]; double zhekou[1<<16 | 1][16]; double toarea[1<<16 | 1]; int bit[1 << 16 | 1]; struct Pizza { double p; double a; double Coupon[16]; }; Pizza pizza[16]; inline int lowbit(int i) { return i&(-i); } void read() { for(int i = 0; i < m; i++) { scanf("%lf%lf%d", &p, &a, &n); pizza[i].p = p; pizza[i].a = a; for(int j = 0; j < n; j++) { scanf("%d%lf", &x, &y); pizza[i].Coupon[x-1] = y; } } } void solve() { int maxs = 1 << m; for(int i = 0; i < m; i++) bit[1 << i] = i; toarea[0] = 0; for(int i = 1; i < maxs; i++) { int id = bit[lowbit(i)]; toarea[i] = toarea[i - lowbit(i)] + pizza[id].a; } dp[0] = 0; for(int s = 1; s < maxs; s++) dp[s] = 1e9; for(int s = 0; s < maxs; s++) { for(int i = 0; i < m; i++) { if((1 << i) & s) continue; zhekou[s][i] = 1.0; if(s) { int lowb = lowbit(s); int id = bit[lowb]; zhekou[s][i] = zhekou[s ^ lowb][i] * (100 - pizza[id].Coupon[i]) / 100.0; } dp[s ^ (1<<i)] = min(dp[s ^ (1<<i)], dp[s] + zhekou[s][i]*pizza[i].p); } } double ans = 1e10; for(int s = 0; s < maxs; s++) { ans=min(ans, dp[s] * 1.0 / toarea[s]); } printf("%.4f\n", ans); } int main() { while(scanf("%d", &m) == 1, m) { read(); solve(); } return 0; }
NOJ 1116 哈罗哈的大披萨 【淡蓝】 状态压缩DP