ZOJ 3769 —— Diablo III(背包,DP)

来源:互联网 发布:微电影知乎 编辑:程序博客网 时间:2024/04/30 19:21

题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3769

额,题目有点长的样子。

大概意思就是说人身上有13个地方可以装备道具,其中两个注意点,一个是fingers,这说明有两根手指可以装备。。。另一个是Two-Handed这种装备会同时占据Weapon和Shield两种道具的装备位置,同一位置只能装备一个道具,不同位置不会影响。

每种道具都会有damage和toughness两个参数,题目要问的是,从中选出一些装备,那么人物的damage值和toughness就是所选装备对应的和,在保证toughness不小于m的情况下,求最大的damage,如果无法达到toughness,就输出-1。


这个背景还是挺经典的背包问题,只是套上一些限制。

如果按照装备的类型分类,最多就13类,先不管前面说的两个注意点,如果这13类之间是没有影响的,dp[i][j]来表示取到第i 类物品,总toughness是j 时候,最优伤害值。

那么对于第i 类物品,假设当前考虑到其中的第k件物品,两个参数分别是d和t,那么枚举前一类物品能够达到的所有状态dp[i-1][j],可以推出新的状态dp[i][j+t] = max(dp[i][j+t], dp[i][j]+d)

而在这个问题中,因为我们只需要达到m,所以对于j+t超过m情况,可以简单记录为m。


然后建立在这种思路的基础上,我们再来考虑那两个条件:

1、对于两个手指的,无论是只装备一根手指,还是装备了两只,都用手指这一类来表示,那么,所有手指装备本身当作只装备一根手指,装备两只的你就两两枚举下吧;

2、对于Weapon和Shield两种道具以及Two-Handed,我们还是把它们当成一种来处理,首先各自肯定当成一件物品,然后就是枚举Weapon和Shield的搭配了;

这样处理之后,按照我们的递推式,同类物品之间是不会有冲突的,所以以上处理过后,题目的限制条件就不见了。。。


然后我们就可以快乐地AC了~~

PS:因为扫物品名称列表的时候,几个特殊处理的物品都是在后面,所以具体到实现的时候,我是从后往前推的。。。

#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;#define pb push_backchar name[20][20]={"Head","Shoulder","Neck","Torso","Hand","Wrist","Waist","Legs","Feet","Finger","Shield","Weapon","Two-Handed"};int t, n, m;struct Goods{    int dmg, tou;}g;int getnum(char* s){    for(int i=0; i<13; i++) if(strcmp(s, name[i])==0)   return i;}vector<Goods> V[13];//用vector来存储同类物品int dp[13][50001], vd, vt;int main(){    int i, j, k;    char s[20];    scanf("%d", &t);    while(t--){        scanf("%d %d", &n, &m);        for(i=0; i<13; i++) V[i].clear();        while(n--){            scanf("%s %d %d", s, &i, &j);            k = getnum(s);            V[k].pb((Goods){i,j});            if(k==11 || k==10){//如果发现Weapon和Shield的,也算到Two-Handed里面去                V[12].pb((Goods){i,j});            }        }        //枚举Weapon和Shield两两组合        for(i=0; i<V[11].size(); i++){            for(j=0; j<V[10].size(); j++){                V[12].pb((Goods){V[11][i].dmg+V[10][j].dmg, V[11][i].tou+V[10][j].tou});            }        }        //把存放Shield的清空,用来记录finger的物品,Weapon的清不清空无所谓        V[10].clear();        for(i=0; i<V[9].size(); i++){            V[10].pb(V[9][i]);            //枚举finger物品的两两组合            for(j=i+1; j<V[9].size(); j++){                V[10].pb((Goods){V[9][i].dmg+V[9][j].dmg, V[9][i].tou+V[9][j].tou});            }        }        V[9].clear();//这个清空,因为finger的情况都存到V[10]里面去了        memset(dp,-1,sizeof(dp));        dp[11][0]=0;        for(i=0; i<V[12].size(); i++){            g = V[12][i];            vt = g.tou>m?m:g.tou;            vd = g.dmg;            dp[11][vt] = max(dp[11][vt], vd);        }        for(k=10; k>=0; k--){            for(j=0; j<=m; j++){                dp[k][j] = max(dp[k][j], dp[k+1][j]);                if(dp[k+1][j]==-1)  continue;                for(i=0; i<V[k].size(); i++){                    g = V[k][i];                    vt = (g.tou+j)>m?m:(g.tou+j);                    vd = g.dmg+dp[k+1][j];                    dp[k][vt] = max(dp[k][vt], vd);                }            }        }        printf("%d\n", dp[0][m]);    }    return 0;}


0 0
原创粉丝点击