原题链接:
https://vjudge.net/contest/541856#problem/H
题意:
有n场考试,还有t天开学
有m本复习资料
对于第i本复习资料,名称是s[i],研究的时间是y[i],可以在这门学科上加x[i]的分(每科的上限是100,每本资料只能用一次)
他的所有科目都是0分,可以挂p门课,否则就要被叫家长
想知道在不叫家长的情况下能得到的最大分,如果被叫了家长输出-1
数据范围:n<=50,m<=15000,s[i]长度不超过15,x[i]和y[i]数据范围是110,天数t:1500,允许不及格科目p:0<=3
思路:
f[i][j]表示第i门课程花费时间不超过j能得到的最大分数
先用一个vector< pii> a[i]来存第i门课程的所有书籍
那么枚举第i门的每个书籍x,每本都有选或者不选的情况,选x的状态转移是:
f[i][j]=max(f[i][j],f[i][j-v[x]]+w[x]);
dp[i][j][pp]表示在前i科里,花费不超过j天的时间,挂了pp门课的最大分数。
枚举第i门课的最大花费时间k,然后得到这个情况的最大分数f[i][k]
当f[i][k]>=60的时候状态转移方程是:dp[i][j][pp]=max(dp[i][j][pp],dp[i-1][j-k][pp]+f[i][k]);
当f[i][k]<=60的时候且pp-1>=0的时候的状态转移方程是:dp[i][j][pp]=max(dp[i][j][pp],dp[i-1][j-k][pp-1]+f[i][k]);
#include<bits/stdc++.h>
#include<vector>
#define v first
#define w second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int n,m,t,p;
int f[60][600];
int dp[60][600][4];
vector<pii> a[60];
map<string,int> mp;
void sove(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
string s;
cin>>s;
mp[s]=i;
a[i].clear();
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
string s;
cin>>s;
int id=mp[s];
int v,w;
scanf("%d%d",&w,&v);
a[id].push_back({v,w});
}
scanf("%d%d",&t,&p);
memset(f,0,sizeof f);
for(int i=1;i<=n;i++){
for(auto vv:a[i]){
int vx=vv.v,wx=vv.w;
for(int j=t;j>=vx;j--){
f[i][j]=max(f[i][j],f[i][j-vx]+wx);
f[i][j]=min(100,f[i][j]);
}
}
}
memset(dp,-0x3f,sizeof dp);
dp[0][0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=t;j++){
for(int k=0;k<=j;k++){
for(int pp=0;pp<=p;pp++){
if(f[i][k]>=60){
dp[i][j][pp]=max(dp[i][j][pp],dp[i-1][j-k][pp]+f[i][k]);
}else if(pp-1>=0){
dp[i][j][pp]=max(dp[i][j][pp],dp[i-1][j-k][pp-1]+f[i][k]);
}
}
}
}
}
int ans=-1;
for(int i=0;i<=p;i++){
ans=max(dp[n][t][i],ans);
}
printf("%d\n",ans);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
sove();
}
return 0;
}