码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 4568 旅行商问题

时间:2015-07-25 16:56:14      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:

一个N*M的迷宫,每个点有代价,代价为-1时表示不能走到,迷宫中有k个宝藏,求取走所有宝藏所需要的最小代价,只能进入迷宫一次

计算出所有宝藏之间的最短距离及从该宝藏出迷宫的最短距离,然后做状压DP即可

#include "stdio.h"
#include "string.h"
#include "queue"
using namespace std;

struct node
{
    int x,y,cost;
    bool friend operator<(node n1,node n2)
    {
        return n2.cost<n1.cost;
    }
};
const int inf=0x3f3f3f3f;
const int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };
int n,m,k,tot;
int dis[20][20],mark[210][210],a[210][210],b[20],x[20],y[20]; // mark记录点是否存在宝藏并记录编号,dis记录宝藏间最短距离
int used[210][210],dp[70000][20]; // dp[i][j]=i状态,以j点为最终结点的最小代价

int Min(int a,int b)
{
    if (a<b) return a;
    else return b;
}
int judge(int x,int y)
{
    if (x<0 || y<0 || x>=n || y>=m) return 0;
    if (used[x][y]!=0) return 0;
    if (a[x][y]==-1) return 0;
    return 1;
}
void bfs(int w)
{
    priority_queue<node>q;
    node cur,next;
    int i;

    memset(used,0,sizeof(used));
    cur.x=x[w];
    cur.y=y[w];
    cur.cost=0;
    if (cur.x==0 || cur.y==0 || cur.x==n-1 || cur.y==m-1) // 是否在边界,在边界则记录到0号点距离
        dis[w][0]=dis[0][w]=dis[w][k+1]=dis[k+1][w]=0;
    used[cur.x][cur.y]=1;
    q.push(cur);
    while (!q.empty())
    {
        cur=q.top();
        q.pop();
        for (i=0;i<4;i++)
        {
            next.x=cur.x+dir[i][0];
            next.y=cur.y+dir[i][1];
            if (judge(next.x,next.y)==0) continue;

            next.cost=cur.cost+a[next.x][next.y];
            used[next.x][next.y]=1;
            if (next.x==0 || next.y==0 || next.x==n-1 || next.y==m-1)
            {
                dis[w][0]=dis[0][w]=Min(dis[w][0],next.cost);
                dis[w][k+1]=dis[k+1][w]=dis[w][0];
            }

            if (mark[next.x][next.y]!=0)
            {
                dis[w][mark[next.x][next.y]]=Min(dis[w][mark[next.x][next.y]],next.cost-a[next.x][next.y]);
                dis[mark[next.x][next.y]][w]=dis[w][mark[next.x][next.y]];
            }
            q.push(next);
        }
    }
}

void DP()
{
    int i,j,l;
    memset(dp,inf,sizeof(dp));
    dp[1][0]=0;
    for (i=0;i<=tot;i++)
        for (j=0;j<=k+1;j++)
        if ( (b[j]&i)!=0 && dp[i][j]!=-1)
            for (l=0;l<=k+1;l++)
            if (l!=j && (b[l]&i)==0)
                dp[i+b[l]][l]=Min(dp[i+b[l]][l],dp[i][j]+dis[j][l]);

}
int main()
{
    int t,i,j,ans;
    b[0]=1;
    for (i=1;i<=16;i++)
        b[i]=b[i-1]*2;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&m);
        for (i=0;i<n;i++)
            for (j=0;j<m;j++)
            scanf("%d",&a[i][j]);
        scanf("%d",&k);
        memset(mark,0,sizeof(mark));
        for (i=1;i<=k;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            mark[x[i]][y[i]]=i;
        }
        memset(dis,inf,sizeof(dis));
        for (i=1;i<=k;i++)
            bfs(i); // 计算所有宝藏间的最短距离及与边界的最短距离
        tot=b[k+2]-1;
        DP(); // 状压DP
        ans=dp[tot][k+1];
        for (i=1;i<=k;i++)
            ans+=a[x[i]][y[i]];
        printf("%d\n",ans);
    }
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

HDU 4568 旅行商问题

标签:

原文地址:http://blog.csdn.net/u011932355/article/details/47057135

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!