题意:一家买馅饼的店,第i 天,种类为j 的馅饼售价Ci,j元,问要想每天都有一个馅饼吃,最少要花多少钱。馅饼可以永久存放不会坏,所以可以提前买很多,同时每天买p个馅饼需要交税p*p。
思路:动态规划,状态定义 dp[i][j] 代表前i天,买j个馅饼所要花费的最少金钱数。即dp[n][n]就是最终的答案了。
转移方程的思路:
1.对于第一天,当然可以选择买1~m个,要使得价钱最低,把价钱从小到大排序即可满足。
2.不是第一天的情况,当前天枚举买的馅饼数目j,同时枚举j里面有k个馅饼是当前天买的,j-k个是前面提前买的。
dp[i][j] = min(dp[i][j], dp[i-1][j - k] + k^2 + k个馅饼的最小价钱)
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <math.h>
#define INF 0x3f3f3f3f
const int N = 310;
using namespace std;
int rec[N][N];
int dp[N][N];
int sum[N][N];
void run()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
scanf("%d", rec[i] + j);
sort(rec[i] + 1, rec[i] + 1 + m);
}
memset(dp, INF, sizeof(dp));
memset(sum, 0, sizeof(sum));
dp[1][1] = rec[1][1] + 1;
sum[1][1] = rec[1][1];
for (int i = 2; i <= m; i++)
{
sum[1][i] = sum[1][i-1] + rec[1][i];
dp[1][i] = sum[1][i] + i*i;
}
//看了别人的代码并没有开sum数组,仔细分析一下
//sum数组其实是不必开的,因为使用时每种求和也
//是只用了一次,用时用一个变量存储之前计算的结果就好了
//不必多开个数组
for (int i = 2; i <= n; i++)
{
sum[i][1] = rec[i][1];
for (int j = 2; j <= m; j++)
sum[i][j] = sum[i][j-1] + rec[i][j];
}
for (int i = 2; i <= n; i++)
for (int j = i; j <= n; j++)
for (int k = 0; k <= j && k <= m; k++)//最多m个馅饼,所以当前k最多是m,j-k要大于等于0
{ //所以k要小于等于j
dp[i][j] = min(dp[i][j], dp[i-1][j - k] + k*k + sum[i][k]);
}
printf("%d\n", dp[n][n]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r" ,stdin);
#endif
freopen("out.txt","w",stdout);
int T, cas = 1;
scanf("%d", &T);
while (T--)
{
printf("Case #%d: ", cas++);
run();
}
return 0;
}