JZOJ3737 挖宝藏(treasure)

该博客介绍了一道名为'JZOJ3737 挖宝藏'的题目,涉及到一种有后效性的动态规划问题。博主详细解释了当高度为1时,问题转化为WC2008游览计划,可以使用dp和spfa松弛方法来解决。当增加层数时,博主提出了在下一层添加虚拟宝藏的策略,通过状态转移方程确保转移的正确性和全面性。最后,博主给出了代码实现的关键部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Description

这里写图片描述
Input
这里写图片描述
Sample Input

2 2 2

10 9

10 10

10 1

10 10

1 1 1

1 2 2
Output

输出一个整数,为矿工获得所有宝藏的最小代价。
Sample Output

30
Data Constraint
这里写图片描述

题解

首先这题h等于1的情况即WC2008游览计划,是有后效性的dp,我们考虑状压+spfa松弛解决
然后这题多了一个层数,我们在把上一层传到下一层时可以考虑在下一层新增一个虚拟的宝藏,这个宝藏可以在任意一个点,然后它代表了上面一层全部宝藏都已经选完了,选完之后选择在这个点走下来

具体的实现就是
f[i][j][s]=f[i][j][s]+f[i][j][ss]a[t][i][j]
其中t表示当前在第t层,并且有s’ and s=s
这就表示了s状态可以由上述两个状态转移而来,而(i,j)点因为走了两次所以减去一次
可以证明这样转移的正确性以及全面性
然后我们对f[x][y][s]做一次spfa松弛就可以了

贴代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;

const int maxn=13;

int f[maxn][maxn][1<<maxn],ff[maxn][maxn][1<<maxn],a[maxn][maxn][maxn];
int t[maxn][maxn][3];
int h[maxn*maxn*30][3];
int g[maxn];
int go[5][3];
bool bz[maxn][maxn];
int i,j,k,l,hh,m,n,x,y,z,c1,c2,c3,p1,p2,p3,ans,tt,tsm;

void spfa(){
    int i,j,k,x1,y1;
    i=tt;
    j=0;
    while (i>j){
        j++;
        x=h[j][1]; y=h[j][2];
        if (bz[x][y]==true) continue;
        bz[x][y]=true;
        for (k=1;k<=4;k++){
            x1=h[j][1]+go[k][1];
            y1=h[j][2]+go[k][2];
            if (x1==0||y1==0||x1>n||y1>m) continue;
            if (f[x][y][c2]+a[c1][x1][y1]<f[x1][y1][c2]){
                f[x1][y1][c2]=f[x][y][c2]+a[c1][x1][y1];
                bz[x1][y1]=false;
                i++;
                h[i][1]=x1; h[i][2]=y1;
            }
        }
    }
}
int main(){
    freopen("treasure.in","r",stdin);
    freopen("treasure.out","w",stdout);
    //freopen("t2.in","r",stdin);
    scanf("%d%d%d",&hh,&n,&m);
    for (i=1;i<=hh;i++){
        for (j=1;j<=n;j++){
            for (k=1;k<=m;k++) scanf("%d",&a[i][j][k]);
        }
    }
    go[1][1]=go[2][1]=0;
    go[3][2]=go[4][2]=0;
    go[1][2]=go[3][1]=1;
    go[2][2]=go[4][1]=-1;
    for (i=1;i<=hh;i++){
        scanf("%d",&g[i]);
        for (j=1;j<=g[i];j++) scanf("%d%d",&t[i][j][1],&t[i][j][2]);
    }
    memset(f,127,sizeof(f));
    memset(ff,127,sizeof(ff));
    tsm=f[1][1][1];
    for (p1=1;p1<=n;p1++){
        for (p2=1;p2<=m;p2++){
            f[p1][p2][0]=0;
        }
    }
    for (i=1;i<=g[1];i++) f[t[1][i][1]][t[1][i][2]][1<<(i-1)]=a[1][t[1][i][1]][t[1][i][2]];
    ans=0x7fffffff;
    for (c1=1;c1<=hh;c1++){
        for (c2=1;c2<=(1<<g[c1])-1;c2++){
            tt=0;
            memset(bz,false,sizeof(bz));
            for (p1=1;p1<=n;p1++){
                for (p2=1;p2<=m;p2++){
                    p3=c2;
                    while (p3){
                        p3=(p3-1)&c2;
                        if (!p3) break;
                        f[p1][p2][c2]=min(f[p1][p2][c2],f[p1][p2][p3]+f[p1][p2][c2-p3]-a[c1][p1][p2]);
                    }
                    if (f[p1][p2][c2]<tsm){
                        tt++;
                        h[tt][1]=p1;
                        h[tt][2]=p2;
                        if (c1==hh & c2==(1<<g[c1])-1)
                            ans=min(ans,f[p1][p2][c2]);

                    }
                }
            }
            spfa();
        }
        c2=(1<<g[c1])-1;
        for (p1=1;p1<=n;p1++){
            for (p2=1;p2<=m;p2++){
                ff[p1][p2][1<<g[c1+1]]=f[p1][p2][c2]+a[c1+1][p1][p2];
            }
        }
        g[c1+1]++;
        for (c2=1;c2<=(1<<g[c1+1])-1;c2++){
            for (p1=1;p1<=n;p1++){
                for (p2=1;p2<=m;p2++){
                    f[p1][p2][c2]=ff[p1][p2][c2];
                }
            }
        }
        memset(ff,127,sizeof(ff));
        for (c2=1;c2<=g[c1+1]-1;c2++){
            f[t[c1+1][c2][1]][t[c1+1][c2][2]][1<<(c2-1)]=a[c1+1][t[c1+1][c2][1]][t[c1+1][c2][2]];
        }
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值