1. 程式人生 > >轉化為分組揹包 zoj 3769

轉化為分組揹包 zoj 3769

題目連結:https://vjudge.net/problem/ZOJ-3769

題意:現在你要去打怪,你有13種裝備,每件裝備會有傷害和防禦兩種屬性,一般來說,每種裝備只可以裝備一件,但是特別的,戒指(Finger)你可以同時裝備兩個,左右手各一個,然後對於“Two-Handed”類的裝備,如果你裝備這種裝備,那麼你就不可以裝備"Shield", "Weapon"這兩種,反之,如果你裝備了"Shield", "Weapon"中的任意一種,那麼你就不可以裝備“Two-Handed”類的裝備,現在需要我們求出在達到m點以上防禦值的情況下可以達到的最大攻擊值。如果無法達到m點防禦值,則輸出-1。

思路:這題可以轉化為分組揹包,但是又兩個問題要解決,一個是Finger類裝備,我們可以把Finger類的裝備兩兩組合,加入Finger類裡面,這樣就把兩個Finger類的裝備轉化為了一個,然後對於"Shield", "Weapon"這兩類,我們也是可以兩兩組合加入“Two-Handed”類裡面去的,當然,為了避免其中一種裝備的數量為0,所以可以先將"Shield", "Weapon"這兩類分別加入“Two-Handed”,然後在加入兩兩組合的。我一開始是用結構體陣列儲存的,一直超時,看了大佬部落格,說要先處理數量多的那一類,這樣可以節省時間,覺得有道理,但是用結構體陣列改了還是一直超時(果然,大佬的程式碼和我的就是不一樣),最後重寫程式碼,改成和他們一樣用vector儲存就過了,不知道為啥。如果有誰知道的,可以提醒一下我,感謝感謝。

這裡開二維陣列,其中把防禦看做揹包容量,但是我們不知道揹包容量的上限,題目只說了要大於等於m點防禦值,所以我們把m以上的都看成m(為啥別人腦洞就這麼大),由於在第k組拿裝備的時候需要知道第k-1組的值,所以狀態轉移方程就是dp[k][i+w[j]]=max(dp[k][i+w[j]],dp[k-1][i]+v[j])。

程式碼:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include
<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 50005 /*struct point{ int u,w; }; bool operator <(const point &s1,const point &s2) { if(s1.v!=s2.v) return s1.v>s2.v; else return s1.u>s2.u; }
*/ struct node{ int v,w; }; int n,m,k,t; map<string,int>mp; vector<node>ve[20]; string ss[20]={" ","Two-Handed","Finger", "Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist", "Legs","Feet", "Shield", "Weapon"}; int dp[15][maxn]; void init() { for(int i=1;i<=13;i++){//給裝備編號 mp[ss[i]]=i; } } int main() { init(); cin>>t; while(t--) { cin>>n>>m; for(int i=0;i<=15;i++){ ve[i].clear(); } string s; int w,v; for(int i=1;i<=n;i++){ cin>>s>>v>>w; int id=mp[s]; ve[id].push_back((node){v,w}); } memset(dp,-1,sizeof(dp));//初始化所有狀態都不可達 dp[0][0]=0;//初始化 for(int i=0;i<ve[12].size();i++){//"Shield", "Weapon"合併到“Two-Handed” ve[1].push_back(ve[12][i]); } int num1=ve[12].size(); int num2=ve[13].size(); for(int i=0;i<num2;i++){ ve[1].push_back(ve[13][i]); for(int j=0;j<num1;j++){ ve[1].push_back((node){ve[13][i].v+ve[12][j].v,ve[13][i].w+ve[12][j].w}); } } num1=ve[2].size(); for(int i=0;i<num1;i++){//"Finger"合併 for(int j=i+1;j<num1;j++){ ve[2].push_back((node){ve[2][i].v+ve[2][j].v,ve[2][i].w+ve[2][j].w}); } } for(int k=1;k<=11;k++){//列舉組 for(int i=0;i<=m;i++){//列舉防禦值 dp[k][i]=max(dp[k][i],dp[k-1][i]);//現在狀態的初值從前一狀態來 if(dp[k-1][i]==-1)//如果前一狀態不可達 continue; for(int j=0;j<ve[k].size();j++){//如果前一狀態可以到達,那麼可以在前一狀態的基礎上在當前組 //拿一件裝備 int min1=min(m,i+ve[k][j].w); dp[k][min1]=max(dp[k][min1],dp[k-1][i]+ve[k][j].v); } } } cout<<dp[11][m]<<endl; } return 0; }

結構體陣列一直超時的程式碼:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 50005
/*struct point{
    int u,w;
};
bool operator <(const point &s1,const point &s2)
{
    if(s1.w!=s2.w)
    return s1.w>s2.w;
    else
    return s1.u>s2.u;
}*/
map<string,int>mp;
int dp[15][maxn];
int n,m,k,t;
struct node{
    int num;
    int w[3005];
    int v[3005];
}zu[20];
string s[18]={" ", "Two-Handed"," ","Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist", 
"Legs", "Feet", "Finger", "Shield", "Weapon"};
void init()
{
    for(int i=1;i<=14;i++)
    {
        mp[s[i]]=i;
    }
}
void combine_finger()
{
    int c=zu[12].num;
    if(c==0)
    return;
    if(c==1)
    {
        zu[2].num++;
        zu[2].w[1]=zu[12].w[1];
        zu[2].v[1]=zu[12].v[1];
        return;
    }
    for(int i=1;i<c;i++){
        for(int j=i+1;j<=c;j++){
            zu[2].num++;
            int k=zu[2].num;
            zu[2].w[k]=zu[12].w[i]+zu[12].w[j];
            zu[2].v[k]=zu[12].v[i]+zu[12].v[j];
        }
    }
    return;
}
void combine_two()
{
    int a=zu[13].num;
    int b=zu[14].num;
    for(int i=min(a,1);i<=a;i++){
        for(int j=min(b,1);j<=b;j++){
            zu[1].num++;
            int c=zu[1].num;
            zu[1].w[c]=zu[13].w[i]+zu[14].w[j];
            zu[1].v[c]=zu[13].v[i]+zu[14].v[j];
        }
    }
    return;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        string ss,w,v; 
        int a,b;
        for(int i=1;i<=n;i++){
            cin>>ss>>a>>b;
            int id=mp[ss];
            zu[id].num++;
            int c=zu[id].num;
            zu[id].v[c]=a;
            zu[id].w[c]=b;
        }
        combine_finger();
        combine_two();
        memset(dp,-1,sizeof(dp));
        dp[0][0]=0;
        for(int k=1;k<=11;k++){
            for(int i=0;i<=m;i++){
                dp[k][i]=max(dp[k][i],dp[k-1][i]);
                if(dp[k-1][i]==-1)
                continue;
                for(int j=1;j<=zu[k].num;j++){
                    int min1=min(m,i+zu[k].w[j]);
                    dp[k][min1]=max(dp[k][min1],dp[k-1][i]+zu[k].v[j]);
                }
            }
        }
        cout<<dp[11][m]<<endl;
    }
    return 0;
}