題目大意
描述
小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的操作很有靈性。