b_vj_Hunter(dij预处理宝藏与宝藏外界的最短距离+状压dp)

有一个n*m的网格,正数格子代表被研究的费用,-1代表不能被研究,且网格中有k个宝藏;问一个宝藏猎人以网格外部为起点进入网格,并收集所有宝藏回到起点的最小花费。(1<=K<=13,n,m<=200)

思路:f[i][j]表示终点在宝藏i,且宝藏状态为j时的最少花费;
由于要回到起点,所以在dijkstra中用out[id]表示从i走到边界的最短距离

#include<iostream>
#include<math.h>
#include<queue>
using namespace std;
const int N=205, M=20, inf=0x3f3f3f3f, dir[4][2] = { {1,0},{0,-1},{0,1},{-1,0} };
int n,m,K,d[N][N],sp[N][N],g[N][N],out[N],f[M][1<<M];
struct node {
    int x,y,w;
}T[N];
struct cmp{
    bool operator()(node& a, node& b){
        return a.w > b.w;
    }
};
void dij(int sx, int sy, int id) {
    for (int i=0; i<n; i++) for (int j=0; j<m; j++) sp[i][j]=inf;
    priority_queue<node, vector<node>, cmp> q;
    q.push({sx,sy,0}), sp[sx][sy]=0;
    while (!q.empty()) {
        int x=q.top().x, y=q.top().y; q.pop();
        if (x==0 || x==n-1 || y==0 || y==m-1)  //宝藏到边界的距离,注g[sx][sy]的花费没有尚未算进去
            out[id]=min(out[id], sp[x][y]);
        for (int k=0; k<4; k++) {
            int tx=x+dir[k][0], ty=y+dir[k][1];
            if (tx>=0 && tx<n && ty>=0 && ty<m && g[tx][ty]!=-1 && sp[tx][ty]>sp[x][y]+g[tx][ty]) {
                sp[tx][ty]=sp[x][y]+g[tx][ty];
                q.push({tx,ty,sp[tx][ty]});
            }
        }
    }
}
int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int q; cin>>q;
    while (q--) {
        cin>>n>>m;
        for (int i=0; i<n; i++)
        for (int j=0; j<m; j++)
            cin>>g[i][j];
        cin>>K;
        for (int i=0; i<K; i++) cin>>T[i].x>>T[i].y;
        //求出宝藏与宝藏之间的最短距离(注:dij只能求单源最短路)
        for (int i=0; i<K; i++) {
            out[i]=inf; //out[i]就是宝藏到外界的最短距离
            dij(T[i].x,T[i].y,i);
            for (int j=0; j<K; j++) {
                d[i][j]=sp[T[j].x][T[j].y];
            }
        }
        int tot=1<<K, ans=inf;
        for (int i=0; i<K; i++) for (int j=0; j<tot; j++) f[i][j]=inf;
        for (int i=0; i<K; i++) f[i][1<<i]=g[T[i].x][T[i].y]+out[i]; //外界到宝藏i的距离
        
        for (int j=0; j<tot; j++)
        for (int s=0; s<K; s++) if (j&(1<<s) && f[s][j]!=inf) {
            for (int e=0; e<K; e++) if ((j&(1<<e))==0) 
                f[e][j|(1<<e)]=min(f[e][j|(1<<e)], f[s][j]+d[s][e]);
        }
        for (int i=0; i<K; i++) ans=min(ans, f[i][tot-1]+out[i]); //终点在i,且从i出去
        cout<<ans<<'\n';
    }
    return 0;
}
posted @ 2020-11-07 16:12  童年の波鞋  阅读(103)  评论(0编辑  收藏  举报