Hunter (HDU - 4568,SPFA + 状压 DP)

一.题目链接:

HDU-4568

二.题目大意:

给一个 n*m 的地图,每个点上有一个数代表花费,-1代表无法经过,有 k 个关键点.

求从地图的任意一侧进入,遍历完所有的关键点,离开地图的最小花费.

三.分析:

设地图外为 0 号关键点,对 k + 1 个关键点分别跑 spfa,得到两两关键点的花费,之后状压即可.

关于状压状态设计,我们选取的是 dp[i][j] 代表点状态为 i,当前在 j 点.

之前把状态设计反,wa 了两晚上....

四.代码实现:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int M = (int)2e2;
const int N = (int)14;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;

int n, m, k;
int a[M + 5][M + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

int cnt;
int head[M * M + 5];
struct node
{
    int v, nx, w;
}Edge[M * M * 4 + M * 8 + 5];

int x[N], y[N];

int cost[N][N];
int dp[1<<N][N];

void init(int n)
{
    cnt = 0;
    for(int i = 0; i <= n; ++i)
    {
        head[i] = -1;
    }
}

void add(int u, int v, int w)
{
    Edge[cnt].v = v;
    Edge[cnt].w = w;
    Edge[cnt].nx = head[u];
    head[u] = cnt++;
}

int id(int i, int j)
{
    return i * m + j + 1;
}

struct Graph
{
    int dis[M * M + 5];
    bool vis[M * M + 5];
    queue <int> q;

    void spfa(int s, int n)
    {
        for(int i = 0; i <= n; ++i) dis[i] = inf, vis[i] = 0;
        q.push(s);
        vis[s] = 1, dis[s] = 0;
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            vis[u] = 0;
            for(int i = head[u]; ~i; i = Edge[i].nx)
            {
                int v = Edge[i].v;
                if(dis[v] > dis[u] + Edge[i].w)
                {
                    dis[v] = dis[u] + Edge[i].w;
                    if(!vis[v])
                    {
                        vis[v] = 1;
                        q.push(v);
                    }
                }
            }
        }
    }
}G[N];

/**
1
2 4
1 3 8 7
1 10 7 6
5
0 0
0 3
0 1
1 2
1 0
**/

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &m);
        init(n * m);
        for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) scanf("%d", &a[i][j]);
        for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j)
        {
            if(a[i][j] == -1)   continue;
            for(int k = 0; k < 4; ++k)
            {
                int x = i + dx[k], y = j + dy[k];
                if(x >= 0 && x < n && y >= 0 && y < m && ~a[x][y])    add(id(i, j), id(x, y), a[x][y]);
            }
        }
        for(int i = 0; i < n; ++i) {if(~a[i][0])    add(id(i, 0), 0, 0); if(~a[i][m - 1])    add(id(i, m - 1), 0, 0);}
        for(int i = 0; i < m; ++i) {if(~a[0][i])    add(id(0, i), 0, 0); if(~a[n - 1][i])    add(id(n - 1, i), 0, 0);}
        scanf("%d", &k);
        for(int i = 1; i <= k; ++i) scanf("%d %d", &x[i], &y[i]), G[i].spfa(id(x[i], y[i]), n * m);
        for(int i = 0; i < n; ++i) {if(~a[i][0])    add(0, id(i, 0), a[i][0]); if(~a[i][m - 1])    add(0, id(i, m - 1), a[i][m - 1]);}
        for(int i = 0; i < m; ++i) {if(~a[0][i])    add(0, id(0, i), a[0][i]); if(~a[n - 1][i])    add(0, id(n - 1, i), a[n - 1][i]);}
        x[0] = 0, y[0] = -1; G[0].spfa(id(x[0], y[0]), n * m);
        ++k; for(int i = 0; i < k; ++i) for(int j = 0; j < k; ++j)  cost[i][j] = G[i].dis[id(x[j], y[j])];
        memset(dp, inf, sizeof(dp));
        dp[1][0] = 0;
        for(int i = 0; i < (1<<k); ++i)
        {
            for(int j = 0; j < k; ++j)
            {
                if(dp[i][j] == inf) continue;
                for(int l = 0; l < k; ++l)
                {
                    if(i & (1<<l))  continue;
                    dp[i|(1<<l)][l] = min(dp[i|(1<<l)][l], dp[i][j] + cost[j][l]);
                }
            }
        }
        int ans = inf;
        for(int i = 0; i < k; ++i)  ans = min(ans, dp[(1<<k) - 1][i] + cost[i][0]);
        printf("%d\n", ans == inf ? 0 : ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/The___Flash/article/details/105479348