首页 > 其他 > 详细

BZOJ 1017 [魔兽地图 DotR]

时间:2020-01-20 21:59:44      阅读:73      评论:0      收藏:0      [点我收藏+]

题面

题意

??有两类共 \(n\) 种物品,所有物品都有一定的价值。第一类物品每种有一定数量和价格,数量上限为 \(k\),第二类物品可以通过已有的物品合成得到,每种物品合成的方案可以用树表示,父节点由所有子节点的装备以一定数量合成。给出所有物品及合成方案,求用 \(m\) 个货币最高能获得的价值。

题解

??树形 DP。令物品 \(u\) 的价值是 \(val_u\),价格(第二类物品的价格定义为所有合成所需物品的价格之和)为 \(cost_u\),合成物品 \(u\) 需要的物品集是 \(S_u\),其中需要物品 \(v\) 参与合成的个数是 \(c_{u,v}\)。考虑用 \(dp1_{u,i,j}\) 代表节点 \(u\) 上的装备选取恰好 \(i\) 个时花费 \(j\) 个货币能获取的最高价值,\(dp2_{u,i,j}\) 代表节点 \(u\) 上的装备选取至少 \(i\) 个时花费 \(j\) 个货币能获取的最高价值,则 \(dp1_{u,i},dp2_{u,i}\) 可以看作 01 背包。令背包中无法被选出的价格组合对应的价值为 \(-\infty\)。显然 \(dp2_{u,i,j}=\max_{k=i}^{\infty} dp1_{u,k,j}\),考虑如何计算 \(dp1_{u,i}\)

??首先,\(dp1_{u,i}\) 只和 \(dp2_{v}(v \in S_u)\) 有关联。令 \(j=i \cdot c_{u,v}\),因为要获得 \(i\) 个物品 \(u\) 至少要保证选取 \(j\) 个物品 \(v\),所以 \(dp1_{u,i}\) 只和 \(dp2_{v,j}\) 有关。而 \(dp1_{u,i}\) 中每一项都需要在每个 \(dp2_{v,j}\) 中选取一个价格,价值的组合求和再加上合成 \(i\) 件物品 \(u\) 带来的额外收益,即 \(val_u-\sum_{v \in S_u}val_v \cdot j\)。其过程相当于把每个 \(dp2_{v,j}\) 的所有价值不为 \(-\infty\) 的项捆绑起来的组合背包问题。用组合背包问题的方式求解,复杂度为 \(O(nkm^2)\)

??虽然对于这道题的数据范围 \(nkm^2\) 高达 \(2 \cdot 10^{10}\),但是由于背包的性质,如果每次都只把背包中不为 \(-\infty\) 的位置提取出来枚举可以大大优化常数。令物品 \(u\) 的价格(第二类物品的价格定义为所有合成所需物品的价格之和)为 \(cost_u\),则 \(dp_{u,i}\) 的背包中所有价值不为 \(-\infty\) 的位置一定分布在 \([i \cdot cost_u,k \cdot cost_u]\),其在 \(dp_u\) 中形成一个高 \(\le k\),宽自定义的倒三角在 \(k*m\) 矩阵中的截面。而将两个倒三角形状的背包做一次组合背包,时间消耗至多是全满的情况的 \(1/6\)。同时,构造出两个接近全满的背包做组合背包的情况至少需要 \(7\) 个节点,而且一个背包用过一次就不能再用第二次。这种做法在原网站上的运行时间是 1300ms,但即便如此,笔者也不能保证这个算法一定是正确的。笔者目前认为,正确算法存在与否,以及笔者算法正确与否的可能性都存在。


代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int maxn=55,maxm=2e3+5,maxl=105;
ll dp[maxn][maxl][maxm];
int n,m;
struct sub{
    int v,c;
    sub(int _v,int _c){
        v=_v; c=_c;
    }
};
vector<sub> e[maxn];
ll val[maxn];
int lim[maxn];
bool vis[maxn];
char s[10];
struct item{
    int w; ll v;
    item(int _w=0,ll _v=0){
        w=_w; v=_v;
    }
};
item a[maxm],b[maxm];
void mult(ll x[],ll y[]){
    int i,j,t,p=0,q=0;
    for (i=0;i<=m;i++){
        if (x[i]>-inf/2) a[p++]=item(i,x[i]);
        if (y[i]>-inf/2) b[q++]=item(i,y[i]);
    }
    fill(x,x+m+1,-inf);
    for (i=0;i<p;i++) for (j=0;j<q&&(t=a[i].w+b[j].w)<=m;j++)
        x[t]=max(x[t],a[i].v+b[j].v);
}
void dfs(int u){
    int i,j,v,c;
    ll det;
    if (!e[u].size()) return;
    lim[u]=maxl; det=val[u];
    for (i=0;i<e[u].size();i++){
        v=e[u][i].v; c=e[u][i].c; dfs(v);
        lim[u]=min(lim[u],lim[v]/c);
        det-=val[v]*c;
    }
    for (i=0;i<=lim[u];i++)
        dp[u][i][0]=i*det;
    for (i=0;i<e[u].size();i++){
        v=e[u][i].v; c=e[u][i].c;
        for (j=0;j<=lim[u];j++)
            mult(dp[u][j],dp[v][j*c]);
    }
    for (i=lim[u];i>0;i--) for (j=0;j<=m;j++)
        dp[u][i-1][j]=max(dp[u][i-1][j],dp[u][i][j]);
}
int main(){
    int i,j,k,t;
    int v,c;
    ll ans;
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=0;i<=n;i++) for (j=0;j<maxl;j++) for (k=0;k<=m;k++)
        dp[i][j][k]=-inf;
    for (i=1;i<=n;i++){
        scanf("%lld%s",&val[i],s);
        if (s[0]=='B'){
            scanf("%d%d",&c,&lim[i]);
            for (j=0;j<=lim[i]&&j*c<=m;j++)
                dp[i][j][j*c]=j*val[i];
            for (j=lim[i];j>0;j--) for (k=0;k<=m;k++)
                dp[i][j-1][k]=max(dp[i][j-1][k],dp[i][j][k]);
        }
        else {
            scanf("%d",&t);
            while (t--){
                scanf("%d%d",&v,&c);
                e[i].push_back(sub(v,c));
                vis[v]=true;
            }
        }
    }
    for (i=1;i<=n;i++) if (!vis[i])
        e[0].push_back(sub(i,1));
    dfs(0); ans=0;
    for (i=0;i<=m;i++) ans=max(ans,dp[0][0][i]);
    printf("%lld\n",ans);
    return 0;
}

BZOJ 1017 [魔兽地图 DotR]

原文:https://www.cnblogs.com/Kilo-5723/p/12219096.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!