hiho #1571 : 小Hi與鋼鐵俠 【貪心】

題目大意

傳送門

描述

小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的操作很有靈性。

發佈了137 篇原創文章 · 獲贊 22 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章