Hrbust 2160 Hunter【最短路SFPA+状压dp】好题!

Hunter
Time Limit: 1500 MSMemory Limit: 32768 K
Total Submit: 7(4 users)Total Accepted: 3(3 users)Rating: Special Judge: No
Description
One day, a hunter named James went to a mysterious area to find the treasures.
James wanted to research the area and brought all treasures that he could.
The area can be represented as a N*M rectangle. Any points of the rectangle is a 
number means the cost of research it,-1 means James can't cross it, James can
start at any place out of the rectangle, and explore point next by next. He will 
move in the rectangle and bring out all treasures he can take. Of course, he will
end at any border to go out of rectangle(James will research every point at anytime
he cross because he can't remember whether the point are researched or not).
Now give you a map of the area, you must calculate the least cost that James bring
out all treasures he can take(one point up to only one treasure).Also, if nothing
James can get, please output 0.
Input
The input consists of T test cases. The number of test cases T is given in the
first line of the input. Each test case begins with a line containing 2 integers N
 M , (1<=N,M<=200), that represents the rectangle. Each of the following N lines
contains M numbers(0~9),represent the cost of each point. Next is K(1<=K<=13),and
next K lines, each line contains 2 integers x y means the position of the treasures,
x means row and start from 0, y means column start from 0 too.
Output
For each test case, you should output only a number 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
Source
2014暑假集训练习赛(8月13日)

题目大意:

有一个N*M大的地图,其中标有-1的位子表示不能走,否则就是走到这个位子需要的花费。

(一个点重复走多次,也要重复花费);

现在主人公可以在任意一个边界的外边作为开始点进入(四个边的外边),一共有K个宝藏,他想要都带走。

问他最小时间花费是多少,从进入到拿到所有宝藏,再到出去(出去也要走到任意一个边界才算走出去);

如果有任意宝藏是拿不到的,输出0.


思路:


1、观察到K并不大,只有13的大小,那么我们肯定对于这部分的处理,套一个状压dp即可。

设定dp【i】【j】表示走完了状态为i的点,最终走到的点的编号是j的最小花费。

那么有:dp【i】【j】=min(dp【i】【j】,dp【l】【k】+dist【k】【j】);

这里要求i中包含j和k,l==i-(1<<j);


2、那么我们需要预处理出dist【i】【j】;

这里大家可以套一个优先队列跑Bfs.

也可以建图跑最短路,我选择建图跑最短路,那么很容易预处理出dist【i】【j】;


3、过程需要多维护一个从宝藏点到最近出口这一条路的长度。

过程维护一下细节处理好即可。

代码肯定很长,如果算法非常熟练的话,这种题是秒的。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
struct node
{
    int from;
    int to;
    int next;
    int w;
}e[505*505*20];
int head[505*505];
int dist[50][50];
int dis[505*505];
int vis[505*505];
int a[505][505];
int num[505][505];
int xx[505];
int yy[505];
int dp[(1<<16)][16];
int n,m,q,cont;
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void Getmap()
{
    cont=0;
    memset(head,-1,sizeof(head));
    int now=1;
    for(int i=0;i<=30;i++)
    {
        for(int j=0;j<=30;j++)
        {
            dist[i][j]=0x3f3f3f3f;
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            num[i][j]=now++;
        }
    }
    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+fx[k];
                int y=j+fy[k];
                if(x>=0&&x<n&&y>=0&&y<m&&a[x][y]!=-1)
                {
                    add(num[i][j],num[x][y],a[x][y]);
                }
            }
        }
    }
}
void SPFA(int ss)
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n*m;i++)dis[i]=0x3f3f3f3f;
    dis[num[xx[ss]][yy[ss]]]=0;
    queue<int >s;
    s.push(num[xx[ss]][yy[ss]]);
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
    for(int z=0;z<q;z++)
    {
        if(z==ss)
        {
            for(int i=0;i<n;i++)
            {
                for(int j=0;j<m;j++)
                {
                    if(i==0||j==0||i==n-1||j==m-1)
                    {
                        dist[z][z]=min(dist[z][z],dis[num[i][j]]);
                    }
                }
            }
            dist[z][z]+=a[xx[z]][yy[z]];
        }
        else
        {
            dist[ss][z]=dis[num[xx[z]][yy[z]]];
        }
    }
}
void Slove_bitDp()
{
    int end=(1<<q);
    for(int i=0;i<end;i++)
    {
        for(int j=0;j<q;j++)
        {
            dp[i][j]=0x3f3f3f3f;
        }
    }
    for(int i=0;i<q;i++)
    {
        dp[(1<<i)][i]=dist[i][i];
    }
    for(int i=0;i<end;i++)
    {
        for(int j=0;j<q;j++)
        {
            if((i&(1<<j))!=0)
            {
                for(int k=0;k<q;k++)
                {
                    if((i&(1<<k))!=0)
                    {
                        int l=i-(1<<j);
                        dp[i][j]=min(dp[i][j],dp[l][k]+dist[k][j]);
                    }
                }
            }
        }
    }
    int output=0x3f3f3f3f;
    for(int i=0;i<q;i++)
    {
        output=min(output,dp[end-1][i]+dist[i][i]-a[xx[i]][yy[i]]);
    }
    if(output==0x3f3f3f3f)printf("0\n");
    else
    printf("%d\n",output);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(num,-1,sizeof(num));
        memset(a,0,sizeof(a));
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        scanf("%d",&q);
        for(int i=0;i<q;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            xx[i]=x;
            yy[i]=y;
        }
        Getmap();
        for(int i=0;i<q;i++)
        {
           SPFA(i);
        }
        Slove_bitDp();
    }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值