題意:一家買餡餅的店,第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;
}