题目大意
描述
小Hi在帮助钢铁侠开发新的盔甲。这套新盔甲一共包含M种武器插槽,其中第i种插槽有Ci个。每个插槽最多安装一个武器模块。
小Hi一共准备了N个武器模块,编号1~N。每个武器模块都有三个参数Vi, Pi和Ti。其中Vi描述了第i个模块的威力,Pi描述了该模块可以安装在哪种插槽中,Ti描述了该模块是否需要JARVIS的支持(1代表需要JARVIS支持,0代表不需要JARVIS支持)。
由于JARVIS的运算能力有限,它最多能同时支持K个武器模块。
现在小Hi想知道,如何装备武器模块才能使总威力最大,同时需要JARVIS支持的模块不超过K个。
输入
输入第一行包含一个整数T,代表测试数据的组数。
对于每组测试数据:
第一行包含三个整数N,M和K。
以下N行每行包含三个整数Vi, Pi和Ti。
最后一行包含M个整数,C1, C2, … CM。
对于30%的数据, T <= 20, N <= 100, M <= 10, K <= 100
对于另70%的数据, T <= 2, 1 <= N <= 105, 1 <= M <= 104, 0 <= K <= 105
对于100%的数据, 1 <= Vi <= 100, 1 <= Pi <= M, 0 <= Ti <= 1, 0 <= Ci <= 100
输出
输出最大的总威力。
思路
错误思路1
开始的时候我想了一个样例为:
1
3 2 1
100 1 1
99 1 0
40 2 1
1 1
即只有2种武器插槽,1个JARVIS支持,2种武器插槽分别最多安装[1 1]个武器模块。
3个武器分别为
100 1 1
99 1 0
40 2 1
这种情况的最优解应该是
第一种武器插槽安装 99 1 0
第二种武器插槽安装 40 2 1
这样直接的贪心(对武器的威力排序,从上往下依次扫一遍得到结果)是得不到正确结果的。
于是我就想贪心,用p_p[i][j]
表示第i
种武器插槽在插的武器总数小于Ci
的基础上,分配给j
个JARVIS支持的时候,威力的最大值。
然后在利用背包算得最后的结果。
可是背包的复杂度太高了,会超时。
正确思路
对每个武器插槽都计算一个不用JARVIS支持的时候威力的最大值。
然后对每个武器插槽都计算:
- 分配第一个JARVIS支持的收益值
- 分配第二个JARVIS支持的收益值
- …
比如第一个武器插槽中:若C[1] = 2,那么不用JARVIS支持的时候威力的最大值为98 + 97
100 1 1
99 1 1
98 1 0
97 1 0
40 1 0
- 分配第1个JARVIS支持的收益为:100 - 97 = 3
- 分配第2个JARVIS支持的收益为:99 - 98 = 1
之后对所有的收益排序,取前K个即可。
代码
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define maxn2 10005
#define min(a,b) (a>b)? b:a
#define max(a,b) (a>b)? a:b
int TT;
int N,M,K;//N个武器 M个插槽 K个jarvis支持
int vectorSize(vector<int> V,int n){
int sum = 0;
for(int i = 0;i<n;i++){
sum+=V[i];
}
return sum;
}
bool cmp(int x1, int x2){
return x1>x2;
}
struct Node{
vector<int> T0;//储存
vector<int> T1;//储存
};
vector<Node> P;
int C[maxn2];
int pre_cnt[maxn2];//第i个武器插槽不使用JARVIS支持的威力
int main (){
scanf("%d",&TT);
while(TT--){
scanf("%d%d%d",&N,&M,&K);//N个武器 M个武器插槽 K个jarvis支持
P.resize(M+1);//
for(int i = 0;i<=M;i++) P.clear();
for(int i = 0;i<N;i++)
{
int temp_vv,temp_pp,temp_tt;
scanf("%d%d%d",&temp_vv,&temp_pp,&temp_tt);
if(temp_tt == 0) P[temp_pp].T0.push_back(temp_vv);//最好是把T0补0至Ci一样多 这样少了很多处理的判断
if(temp_tt == 1) P[temp_pp].T1.push_back(temp_vv);
}
for(int i = 1; i <= M; i++){
scanf("%d",&C[i]);
}
//若T0<Ci
//最好是把T0补0至Ci一样多 这样少了很多处理的判断
//判断有:直接加上T1的第一个字母或者是替换。
for(int i = 0;i <= M; i++){
int T0_size = P[i].T0.size();
for(int j = 0; j<C[i]-T0_size; j++) P[i].T0.push_back(0);
}
for(int i = 1; i<=M;i++){
sort(P[i].T0.begin(),P[i].T0.end(),cmp);
sort(P[i].T1.begin(),P[i].T1.end(),cmp);
// for(int j = 0;j<P[i].T0.size();j++){
// printf("%d:[%d 0]\n",i,P[i].T0[j]);
// }
// for(int j = 0;j<P[i].T1.size();j++){
// printf("%d:[%d 1]\n",i,P[i].T1[j]);
// }
}
vector<int> Profit;
Profit.clear();
for(int i = 1; i<=M;i++){//第i个武器插槽
int size_0 = P[i].T0.size();//不需要JARVIS支持的武器个数
int size_1 = P[i].T1.size();//需要JARVIS支持支持的武器的个数
pre_cnt[i] = vectorSize(P[i].T0,C[i]);;//第i个武器插槽不使用JARVIS支持的总威力
//开始替换
for(int j = 0;j<size_1;j++){
if((C[i]-j-1 >= 0)&&P[i].T1[j] - P[i].T0[C[i]-j-1] > 0) Profit.push_back(P[i].T1[j] - P[i].T0[C[i]-j-1]);
}
}
int Profit_sum = 0;
//取前K个正的Profit作为利息的总值。
//for(int i = 0;i<Profit.size();i++) printf("%d \n",Profit[i]);
sort(Profit.begin(),Profit.end(),cmp);
K = min(Profit.size(),K);
for(int i = 0; i < K; i++) Profit_sum+=Profit[i];
for(int i = 1; i <= M; i++) Profit_sum+=pre_cnt[i];
printf("%d\n",Profit_sum);
}
}
/*
1
5 2 1
100 1 1
99 1 1
98 1 0
97 1 0
40 2 1
3 1
*/
Hit
这个在T0的后面补0的操作很有灵性。