题目链接:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4008
#include <cstdio>
#include <map>
#include <utility>
#include <cstring>
using namespace std;
// my_map[(i,j)]代表从第i,i+1, ..., n首歌选,得到小于总时间长度情况下的最多曲目和对应时间
//map<pair<int,int>, pair<int, long long> > my_map;
// 总时间长度
int total_time = 0;
// 曲目数目(不包括Jin Ge Jin Qu)
int n;
// time[i]代表第i首歌时间
int time[60];
pair<int, long long> get_time(int i, int j);
// r_num/r_time[i][j]代表从第i,i+1, ..., n首歌选,得到小于总时间长度情况下的最多曲目和对应时间
int r_num[60][10000];
int r_time[60][10000];
int main()
{
int T;
scanf("%d", &T);
int count = 0;
while(count < T)
{
// my_map = map<pair<int,int>, pair<int, long long> >();
memset(r_num, -1, sizeof(r_num));
scanf("%d%d", &n, &total_time);
for(int i = 1; i <= n; i++)
scanf("%d", &time[i]);
// 计算结果
pair<int,long long> result = get_time(1, total_time);
printf("Case %d: %d %lld\n", count+1, result.first+1, result.second+678);
count++;
}
return 0;
}
// 计算结果
// 计算从第i首歌开始选,小于j时间的最多曲目和相应时间
pair<int, long long> get_time(int i, int j)
{
/* pair<int,int> this_p = pair<int,int>(i, j);
if(my_map.find(this_p) != my_map.end())
return my_map[this_p];
*/
if(r_num[i][j] != -1)
return pair<int, long long>(r_num[i][j], r_time[i][j]);
if(i == n)
{
if(j <= time[n])
{
// my_map[this_p] = pair<int,long long>(0, 0);
r_num[i][j] = 0;
r_time[i][j] = 0;
}
else
{
// my_map[this_p] = pair<int,long long>(1, time[n]);
r_num[i][j] = 1;
r_time[i][j] = time[n];
}
// return my_map[this_p];
return pair<int, long long>(r_num[i][j], r_time[i][j]);
}
pair<int, long long> result;
pair<int,long long> r1 = get_time(i+1, j);
if(j > time[i])
{
pair<int,long long> r2 = get_time(i+1, j-time[i]);
r2.first++;
r2.second = r2.second + time[i];
if(r1.first > r2.first)
{
// my_map[this_p] = r1;
result = r1;
}
else if(r1.first < r2.first)
{
// my_map[this_p] = r2;
result = r2;
}
else
{
if(r1.second >= r2.second)
{
// my_map[this_p] = r1;
result = r1;
}
else
{
// my_map[this_p] = r2;
result = r2;
}
}
}
else
{
// my_map[this_p] = r1;
result = r1;
}
r_num[i][j] = result.first;
r_time[i][j] = result.second;
// return my_map[this_p];
return result;
}
首先需要想一下Jin Ge Jin Qu必定被选。
假设Jin Ge Jin Qu不被选,现在选择的歌曲是a1,a2, ..., ax.
假设a1至ax时间不超过t, 那么可以再选Jin Ge Jin Qu最优。
假设a1至ax时间超过t, 那么a1至ax-1时间不超过t,由于ax时间小于Jin Ge Jin Qu, 那么将ax替换为Jin Ge Jin Qu得到的解更优。
因为Jin Ge Jin Qu必被选,不妨设其为最后一首被选,那么必须保证从n首歌选得歌的总时间长度小于t.
问题转换为从n首歌选歌,总时间长度小于t的情况下选择最多曲目,并保证最多曲目情况下选择总时间最长的。
为01背包问题。关于01背包问题为什么不能用贪心做,需要思考一下反例,及其与能用贪心做的部分背包问题作对比。
题目设置t可以为10^9, 一开始用map超时,后来看书才知道题目有很隐蔽的提示使得t最多不超过180*n+678.