题意:有N首歌(按照写作的顺序给出每首歌的时间),还有M个压缩碟,每个压缩碟能容纳T分钟的歌曲。歌曲不允许跨碟存储,而且要按照写作顺序存储。求这M个压缩碟最多可以存储多少首歌?
解题思路:
- DP问题,开始不知到怎么做,于是参考了USACO中Skywalker的“一种时间复杂度O(n^2)的DP算法”(http://www.nocow.cn/index.php/USACO/rockers)
- f(i, j) = (a, b)代表在最优情况下,从前i首歌曲中存储j首歌曲后,已经占用a个压缩碟,且第a+1个压缩碟中已经占据b时间
- 边界条件j=0时,f(i, j) = (0, 0)
- 滚雪球的过程:f(i, j) = min(f(i - 1, j), f(i - 1, j - 1) + cost_of_song(i))。也就是将f(i, j)分为不包括第i首歌以及包括第i首歌两种情况。求最小值要比较(a, b)这个数对,首先比较a,在a相等的情况下再比较b
- 最后从N到0遍历f[N][j],找到第一个满足条件的数对(a, b)那么对应的j就是最终的答案
代码:
/*
ID: zc.rene1
LANG: C
PROG: rockers
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 20
int main(void)
{
FILE *fin, *fout;
int N, T, M;
int songs[MAX + 1];
int f[MAX + 1][MAX + 1][2];
int i, j, temp_m, temp_t;
fin = fopen("rockers.in", "r");
fout = fopen("rockers.out", "w");
/*get the input*/
fscanf(fin, "%d %d %d", &N, &T, &M);
for (i=1; i<=N; i++)
{
fscanf(fin, "%d", &songs[i]);
}
/*begin DP*/
for (i=0; i<=N; i++)
{
for (j=0; j<=N; j++)
{
if (j == 0)
{
memset(f[i][j], 0, 2 * sizeof(int));
}
else
{
f[i][j][0] = M;
f[i][j][1] = T;
}
}
}
for (i=1; i<=N; i++)
{
for (j=1; j<=i; j++)
{
temp_m = f[i-1][j-1][0];
temp_t = f[i-1][j-1][1];
if (songs[i] > T)
{
temp_m = M;
temp_t = T;
}
else
{
if ((temp_t + songs[i]) <= T)
{
temp_t += songs[i];
}
else
{
temp_m++;
temp_t = songs[i];
}
}
if (f[i-1][j][0] < temp_m)
{
memcpy(f[i][j], f[i-1][j], 2 * sizeof(int));
}
else if (f[i-1][j][0] == temp_m)
{
if (f[i-1][j][1] > temp_t)
{
f[i][j][0] = temp_m;
f[i][j][1] = temp_t;
}
else
{
memcpy(f[i][j], f[i-1][j], 2 * sizeof(int));
}
}
else
{
f[i][j][0] = temp_m;
f[i][j][1] = temp_t;
}
}
}
for (j=N; j>=0; j--)
{
if (f[N][j][0] < M)
{
fprintf(fout, "%d\n", j);
break;
}
}
if (j == -1)
{
fprintf(fout, "0\n");
}
return 0;
}