[状压DP]JZOJ 3737 挖宝藏(treasure)

Description

 

Input

Output

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

Sample Input

2 2 2
10 9
10 10
10 1
10 10
1 1 1
1 2 2

Sample Output

30
 

Data Constraint

分析

这题的思路很妙!

考虑一层的

设f[x][y][S]表示包含x,y的区域中宝藏集合为S的最小代价

那么转移有f[x][y][S]=min{f[x][y][S']+f[x][y][S^S']-a[x][y] S'≠Ø

然后从各个方向转移过来

容易发现第二种转移可以用SPFA松弛

那么多层的怎么办?首先多开一维记录层数(其实可以滚动),然后有f[h-1][x][y][1]=f[h][x][y][S]+a[h-1][x][y] (S是取得下层所有宝藏的集合)

我们设一个特殊的宝藏0号作为下面所有宝藏,这样就很方便了!

 

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define rint register int
using namespace std;
const int N=11;
struct Treasure {
    int x,y;
    Treasure operator + (Treasure b) {return (Treasure){x+b.x,y+b.y};}
}t[N][N],d[4]={(Treasure){0,1},(Treasure){0,-1},(Treasure){1,0},(Treasure){-1,0}};
int f[N][N][N][1<<N],a[N][N][N],k[N];
bool vis[N][N];
int h,n,m;

bool Check(Treasure x) {return x.x<1||x.x>n||x.y<1||x.y>m;}

void SPFA(int h,int S,Treasure pos) {
    int sum=0,cnt=0;
    deque<Treasure> q;
    while (!q.empty()) q.pop_back();
    q.push_back(pos);vis[pos.x][pos.y]=1;sum+=f[h][pos.x][pos.y][S];cnt++;
    while (!q.empty()) {
        Treasure u=q.front();q.pop_front();
        if (f[h][u.x][u.y][S]>sum/cnt) {
            q.push_back(u);continue;
        }
        sum-=f[h][u.x][u.y][S];cnt--;
        for (int i=0;i<4;i++) {
            Treasure v=u+d[i];
            if (Check(v)) continue;
            if (f[h][v.x][v.y][S]>f[h][u.x][u.y][S]+a[h][v.x][v.y]) {
                f[h][v.x][v.y][S]=f[h][u.x][u.y][S]+a[h][v.x][v.y];
                if (!vis[v.x][v.y]) {
                    if (!q.empty()&&f[h][q.front().x][q.front().y][S]>f[h][v.x][v.y][S]) q.push_front(v);
                    else q.push_back(v);
                    sum+=f[h][v.x][v.y][S];cnt++;
                }
                vis[v.x][v.y]=1;
            }
        }
        vis[u.x][u.y]=0;
    }
}

void Solve() {
    memset(f,0x7f,sizeof f);
    for (rint i=1;i<=n;i++)
        for (rint j=1;j<=m;j++) f[h][i][j][1]=a[h][i][j];
    for (rint i=h;i;i--) {
        for (rint j=1;j<=k[i];j++)
            f[i][t[i][j].x][t[i][j].y][1<<j]=a[i][t[i][j].x][t[i][j].y];
        for (rint S=1,mxn=1<<k[i]+1;S<mxn;S++)
            for (rint x=1;x<=n;x++)
                for (rint y=1;y<=m;y++) {
                    for (rint S1=1;S1<S;S1++)
                        if ((S&S1)==S1) f[i][x][y][S]=min(f[i][x][y][S],f[i][x][y][S1]+f[i][x][y][S^S1]-a[i][x][y]);
                    SPFA(i,S,(Treasure){x,y});
                }
        for (rint x=1;x<=n;x++)
            for (rint y=1;y<=m;y++) f[i-1][x][y][1]=f[i][x][y][(1<<k[i]+1)-1]+a[i-1][x][y];
    }
}

int main() {
    freopen("treasure.in","r",stdin);
    freopen("treasure.out","w",stdout);
    scanf("%d%d%d",&h,&n,&m);
    for (int i=1;i<=h;i++)
        for (int j=1;j<=n;j++)
            for (int k=1;k<=m;k++) scanf("%d",&a[i][j][k]);
    for (int i=1;i<=h;i++) {
        scanf("%d",&k[i]);
        for (int j=1;j<=k[i];j++) scanf("%d%d",&t[i][j].x,&t[i][j].y);
    }
    Solve();z
    int ans=2147483647;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) ans=min(ans,f[0][i][j][1]);
    printf("%d",ans);
}
View Code

 

posted @ 2019-08-12 07:44  Vagari  阅读(128)  评论(0编辑  收藏  举报