HDU 4568 Hunter

Problem Description

One day, a hunter named James went to amysterious area to find the treasures. James wanted to research the area andbrought all treasures that he could.
The area can be represented as a N*Mrectangle. Any points of the rectangle is a number means the cost of researchit,-1 means James can't cross it, James can start at any place out of therectangle, and explore point next by next. He will move in the rectangle andbring out all treasures he can take. Of course, he will end at any border to goout of rectangle(James will research every point at anytime he cross because hecan't remember whether the point are researched or not).
Now give you a map of the area, you mustcalculate the least cost that James bring out all treasures he can take(onepoint up to only one treasure).Also, if nothing James can get, please output 0.

 

 

Input

The input consists of T test cases. Thenumber of test cases T is given in the first line of the input. Each test casebegins with a line containing 2 integers N M , (1<=N,M<=200), thatrepresents the rectangle. Each of the following N lines contains M numbers(0~9),representthe cost of each point. Next is K(1<=K<=13),and next K lines, each linecontains 2 integers x y means the position of the treasures, x means row andstart from 0, y means column start from 0 too.

 

 

Output

For each test case, you should output only anumber means the minimum cost.

 

 

Sample Input

2

3 3

3 2 3

5 4 3

1 4 2

1

1 1

3 3

3 2 3

5 4 3

1 4 2

2

1 1

2 2

 

 

Sample Output

8

11

题目大意:给一个N*M的矩形,矩形的每个点都有一个值,表示到这个点需要用的花费。现在给定K个宝物的坐标,要求从任意位置进入矩阵取完这些宝物,再从任意位置出来需要的最少花费。

这里题意略有问题,前面说有-1存在,-1表示不能通过。根据实际提交,也可以得到没有-1将宝物分隔开(但是有-1存在).所以,所有物品都可以取得。

 

以前做了一道相似的。hdu4771 http://acm.hdu.edu.cn/showproblem.php?pid=4771这题是给4个点。当时就直接暴力枚举的。这道题最多有13个显然就不能暴露枚举了。

 

首先,我们要算出每点之间的最短距离,以及每点到边界的最短距离。这些点的编号为0,1…k-1。我们可以假设边界为k。这样,问题就转化成了一个TSP问题,从k点出发,遍历所有点一次且仅一次后再返回k的最短距离。

故就先用SPFA求出0~k任意两点之间的最短距离。graph存的是每个点的值,将二维转化成为一维存储的。v存的是宝藏所在点的值。dist存的0~k任意两点间的距离。

 

求得0~k任意两点之后,就是一个TSP问题了。因为只有13个点。我们可以进行状态压缩。dp[1<<i][k](i=0~k-1)=dist[i][k],第一步是从k到i点。dp[i][j]表示的就是当前在j点,已经遍历过的点的编号为i的二进制。如,已经去过1,3,5这三个点,在3这个点上,那么就为dp[84][3],84的二进制位101010.代表了1,3,5在三个点已经被遍历过。

转移方程:dp[i][r] = min(dp[i][r],dp[i^(1<<r)][j]+ dist[r][j] - v[j]);因为进去出来都算了j这个点的值的,要减去v[j],后面减去v[i]同理。

最后就得到dp[(1<<k)-1][0~k-1],这是所有点都遍历完了。接着,就回到k点.

ans = min(ans ,dp[(1<<k)-1][i]+dist[i][k]-v[i])(i=0~k-1)。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define INF 99999999
using namespace std;

typedef struct NODE
{
    int x ,y;
};

NODE nodes[14];
int t ,n ,m ,k;
int graph[40010];//graph存的是每一点的值。将二维转化为一维做的
//dist是0~k任意两点之间的最短距离。dis是进行SPFA时,每一点到当前点的最短距离。v是宝藏所在点的值。dp[i][j]表示的是当前走过的点的是i的二进制,在点j点上的最短距离
int dist[14][14] ,dp[1<<14][14] ,vis[40010] ,dis[40010] ,v[40010];
int d[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

void spfa(int s)
{
    int x ,y ,xx ,yy ,newd ,temp;
	memset(vis,0,sizeof(vis));
	for(int i = 0;i<=n*m;i++)
	{
		dis[i] = INF;
	}
	if(graph[s]==-1)
    {
        return;
    }
	dis[s] = graph[s];
	queue<int> q;
	q.push(s);
	vis[s] = 1;
	while(!q.empty())
	{
		temp = q.front();
		q.pop();
		if(temp==n*m)
        {
            continue;
        }
		vis[temp] = 0;
		x = temp / m;
		y = temp % m;
		for(int i = 0;i<4;i++)
		{
		    xx = x + d[i][0];
		    yy = y + d[i][1];
		    if(xx>=0 && xx < n && yy>=0 && yy < m)
            {
                newd = xx * m + yy;
            }
		    else
            {
                newd = n * m;
            }
            if(graph[newd]==-1)
            {
                continue;
            }
			if(dis[newd] - graph[newd] > dis[temp])
			{
				dis[newd] = graph[newd] + dis[temp];
				if(!vis[newd])
				{
					q.push(newd);
					vis[newd] = 1;
				}
			}
		}
	}
};

int main()
{
    int re ,no;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i = 0;i < n;i++)
        {
            for(int j = 0;j < m;j++)
            {
                scanf("%d",&re);
                graph[i * m + j] = re;
            }
        }
        graph[n * m] = 0;
        scanf("%d",&k);
        for(int i = 0;i < k;i++)
        {
            scanf("%d%d",&nodes[i].x,&nodes[i].y);
            v[i] = graph[nodes[i].x * m + nodes[i].y];
        }
        no = 0;
        for(int i = 0;i < k;i++)
        {
            spfa(nodes[i].x * m + nodes[i].y);
            for(int j = i+1;j < k;j++)
            {
                dist[j][i] = dist[i][j] = dis[nodes[j].x * m + nodes[j].y];
            }
            dist[k][i] = dist[i][k] = dis[n*m];
            if(dist[i][k]!=INF)
            {
                no++;
            }
        }
        if(!no)
        {
            printf("0\n");
        }
        else
        {
            no = 1<<k;
            for(int i = 0;i<=no;i++)
            {
                for(int j = 0;j<=k;j++)
                {
                    dp[i][j] = INF;
                }
            }
            for(int i = 0;i<k;i++)
            {
                dp[1<<i][i] = dist[i][k];
            }
            for(int i = 1;i < no;i++)
            {
                for(int j = 0;j < k;j++)
                {
                    for(int r = 0;r < k;r++)
                    {
                        if((i&(1<<j)) && (i&(1<<r)) && j!=r && dp[i^(1<<r)][j]!=INF)
                        {
                            if(dp[i][r] > dp[i^(1<<r)][j] + dist[r][j] - v[j])
                            {
                                dp[i][r] = dp[i^(1<<r)][j] + dist[r][j] - v[j];
                            }
                        }
                    }
                }
            }
            int ans = INF;
            for(int i = 0;i<k;i++)
            {
                if(ans > dp[no-1][i] + dist[i][k] - v[i])
                {
                    ans = dp[no-1][i] + dist[i][k] - v[i];
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}


 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值