问题描述
贵系一年一度的学生节要开始了!!!
这次学生节的节目一共有n个,由于贵系人才辈出,n貌似可能很大的样子哦……
有一名小尚同学,由于他是从数学系转来贵系的,所以他被学生会主席马总勒令只能观看m个节目。
小尚同学在失望之余,从体育苦力部的翔副主席那里偷来了一份节目单,并给每个节目都作了价值评估。
他又发现学生节貌似要搞到很晚很晚,要是全看完的话就不能去洗澡了,这多么悲伤啊!
于是他规定了一个自己能看的最晚的节目号(小尚同学说他洗澡的时间会随心情而定,所以有多种可能)。
他希望能观看到节目价值和最高。
输入格式
输入共n+T+1行。
第一行有三个整数n和m和T,表示一共有n个节目,小尚同学最多能看m个节目小尚同学约定了T个最晚时间。
之后n行每行有一个整数Vi,表示小尚同学心目中第i个节目的价值。
之后T行每行有一个整数ti,表示每个最晚时间的节目号。
输出格式
输出共T行,每行有一个整数,表示对于每一个ti,小尚同学看到节目价值和的最大值。
样例输入
2 1 2
9
10
1
2
样例输出
9
10
样例说明
小尚设定最晚的节目号看到1号的最大价值为9,看到2号的最大价值为10。
数据规模和约定
0<=m<n<=1001,0<=Vi<=1001,1<=T<=1001,1<=ti<=n。
代码逐解
获取数据
为什么要使用map
?
因为最晚节目编号是乱序,但每行输出要根据输入的顺序来,使用map
就解决了这个问题。
int n,m,T,i,j; // i,j是遍历用
cin >> n >> m >> T;
if(m==0){ // 如果会长不让看,就是0
for(i=0;i<T;i++){
cout.width(0); // 防止缩进
cout << 0 <<endl;
}
return 0;
}
vector< int >val(n),time(T); // 保存价值和最晚节目编号
vector< int > dp(m+1,0); // dp数组,使用滚动数组递推
// 输出结果的时候根据time元素的顺序输出
map<int,int> timemap; // 将每个最晚节目编号作为key,对应的最大价值作为value
for(i=0;i<n;i++) // 输入不用说
cin >> val[i];
int tmax = 0; // 保存T中出现的最大节目编号 ,用于后面优化
for(i=0;i<T;i++){
cin >> time[i]; // 输入不用说
tmax = max(tmax,time[i]); // 保留最大节目编号
timemap[time[i]] = INF; // 初始化标识
}
简单优化递推
这个问题是典型的01背包问题,我这里使用了滚动数组优化了空间。
还是再说一下背包问题的状态定义吧。
d
p
(
i
,
j
)
dp(i,j)
dp(i,j)表示前i个节目,在可以看j个的情况下的最大价值。
如果没有节目
i
=
0
i=0
i=0或者一个节目都看不了
j
=
0
j=0
j=0,
d
p
(
i
,
j
)
dp(i,j)
dp(i,j)的最大价值就只能是0;
而面对任意节目i
都可以选择看或不看:
- 选择看第i个节目
可以看的次数就要减1,即 j − 1 j-1 j−1,然后去寻找上一个状态 i − 1 i-1 i−1中可以观看次数剩余 j − 1 j-1 j−1的最优解并加上看的节目的价值,即 d p ( i − 1 , j − 1 ) + v a l [ i ] dp(i-1,j-1)+val[i] dp(i−1,j−1)+val[i] - 选择不看
可以看的次数不用减少,节目i
的价值也不用加上,那么它的最优解就来源于上一次 i − 1 i-1 i−1时,可以观看节目数量为 j j j的最优解,即 d p ( i − 1 , j ) dp(i-1,j) dp(i−1,j)
所有我们选择这两种状态里最大值作为
d
p
(
i
,
j
)
dp(i,j)
dp(i,j)的最优解即可:
d
p
(
i
,
j
)
=
m
a
x
(
d
p
(
i
−
1
,
j
)
,
d
p
(
i
−
1
,
j
−
1
)
)
dp(i,j) = max(dp(i-1,j),dp(i-1,j-1))
dp(i,j)=max(dp(i−1,j),dp(i−1,j−1))
而我这里使用了滚动数组,每次循环前,未被更新的位置实际上就是i-1
。
// 这里是一个优化
n = n>tmax?tmax:n;
/*
如果n大于max(time),只需要递推到max(time)就行了,后面的节目小明也不会去看
如果n小于 max(time),这就是出现了 :
小尚给自己设置的最晚看到哪个节目的节目编号,可能超过了存在的节目编号
*/
for(i=1;i<=n;i++){ // 有前i个节目时
for(j=m;j>0;j--){ // 最多看j个节目时
/*
每个节目有看和不看两种情况
*/
dp[j] = max(dp[j],dp[j-1]+val[i-1]);
}
// 保存节目编号的最大值
if(timemap.find(i)!=timemap.end()) timemap[i] = dp[m];
}
完整代码
#include <iostream>
#include <vector>
#include <map>
/*
INF用于标识,避免出现最晚节目编号的最大值大于最大节目编号,
意思就是,小尚给自己设置的最晚看到哪个节目的节目编号,可能超过存在的节目编号
*/
#define INF -1000
using namespace std;
int max(int a,int b){
return a>b?a:b;
}
int main()
{
int n,m,T,i,j; // i,j是遍历用
cin >> n >> m >> T;
if(m==0){ // 如果会长不让看,就是0
for(i=0;i<T;i++){
cout.width(0); // 防止缩进
cout << 0 <<endl;
}
return 0;
}
vector< int >val(n),time(T); // 保存价值和最晚节目编号
vector< int > dp(m+1,0); // dp数组,使用滚动数组递推
// 输出结果的时候根据time元素的顺序输出
map<int,int> timemap; // 将每个最晚节目编号作为key,对应的最大价值作为value
for(i=0;i<n;i++) // 输入不用说
cin >> val[i];
int tmax = 0; // 保存T中出现的最大节目编号 ,用于后面优化
for(i=0;i<T;i++){
cin >> time[i]; // 输入不用说
tmax = max(tmax,time[i]); // 保留最大节目编号
timemap[time[i]] = INF; // 初始化标识
}
// 这里是一个优化
n = n>tmax?tmax:n;
/*
如果n大于max(time),只需要递推到max(time)就行了,后面的节目小明也不会去看
如果n小于 max(time),这就是出现了 :
小尚给自己设置的最晚看到哪个节目的节目编号,可能超过了存在的节目编号
*/
for(i=1;i<=n;i++){ // 有前i个节目时
for(j=m;j>0;j--){ // 最多看j个节目时
/*
每个节目有看和不看两种情况
*/
dp[j] = max(dp[j],dp[j-1]+val[i-1]);
}
// 保存节目编号的最大值
if(timemap.find(i)!=timemap.end()) timemap[i] = dp[m];
}
for(i=0;i<time.size();i++){ // 根据time元素的顺序输出
if(timemap[time[i]] == INF) // 如果该最晚节目编号不存在,则按存在的最大节目编号放回最优解
cout << dp[m] << endl;
else
cout << timemap[time[i]] << endl; // 按节目编号的顺序输出看到每个节目编号的最大价值
}
return 0;
}
有不懂的欢迎提问,有错误欢迎指出。