贴个唐牛的代码,这道题我一开始理解错了,原以为是由m种烧饼,其实是m个烧饼,所以不存在一个烧饼被买两次的情况。。。。。 确实是很好dp题啊,而且唐牛的解法也很好,我研究了好久才搞明白。。。。。。 //最优性原理:若m=5状态为10110的总花费最小值等于min(10100+cost[2]、10010+const[3]、00110+cost[5]) //这里的下标表示与与程序中的有点不同。。。。 #include <iostream> #include <cassert> using namespace std; const int M = 16; double d[M][M], p[M], a[M], dp[1<<M]; int main() { int m; while (cin >> m) { if (m == 0) break; assert(0 < m && m <= 15);//所有assert为了测试输入的数据是否符合要求,与程序算法无关 for (int i=0; i<m; i++) for (int j=0; j<m; j++) d[i][j] = 1.0;//i,j两个烧饼之间边得距离,够着有向图; for (int i=0; i<m; i++) { int n; cin >> p[i] >> a[i] >> n; assert(1 <= p[i] && p[i] <= 10000); assert(1 <= a[i] && a[i] <= 10000); assert(0 <= n && n < m); for (int j=0; j<n; j++) { int x, y; cin >> x >> y; assert(0 < x && x <= m); assert(1 <= y && y <= 50); --x;//保证第一个烧饼的编号是0 assert(d[i][x]== 1.0); d[i][x] *= (100 - y) * 0.01;//更改边得距离,y代表要打的折扣。。 } } double res = 1e20; int k = 1 << m;//这里是用二进制表示烧饼是否被买的状态,001代表买1不买2、3 //视乎认为每个烧饼只能买一次,k为能买烧饼的状态数 for (int i=0; i<k; i++) dp[i] = res; dp[0] = 0.0; //买哪些烧饼确定后,必须还得确定买烧饼的顺序方案(动态规划),才能确定最后的总花费, //买的烧饼确定后,总的面积就确定了,求出里面总花费最少的顺序方案,最后在求平均花费。 for (int mask=0; mask<k; mask++)//没一个mask都有m位 { double area = 0.0; for (int i=0; i<m; i++)//这个循环里求的是:在mask的情况下递推出再买一个还没买的苹果的最小总花费。 { if (mask & (1 << i))//1移动0为表示的就是第1个烧饼 area += a[i];//加a[i]代表买了第i个烧饼,mask为当前所买烧饼的状态; else { double cost = p[i]; for (int j=0; j<m; j++) if (mask & (1 << j))//1向左移动j位; cost *= d[j][i];//cost表示的是此时(已经买了第j个烧饼)买第i块烧饼的花费, if (dp[mask] + cost < dp[mask | (1 << i)]) dp[mask | (1 << i)] = dp[mask] + cost; } } if (mask)//排除一个也不买的情况 { double tmp = dp[mask] / area; if (tmp < res) res = tmp; } } cout.setf(ios::fixed); cout.precision(4); cout << res << endl; } return 0; }