hdu 4568 Hunter

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4568

题意:给定一个矩阵图,每走一个格子有一个花费,可以从任意边进入,任意边出去,并且图中有k个点要求全部走到,问最少的花费是多少,如果不能够全部走到,输出0。

思路:先用spfa对图做预处理,求出所有必须走的点和地图外构成的图的任意两点的最短路,然后就是枚举必须走的点的访问顺序问题,由于k很小,可以用状态压缩DP,dp[i][j]表示最后选的点是i,j表示已经选的点。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int inf=2099999999;

int s[205][205],ma[205][205];
int dis[15][15],c[15];
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m;
int f[15];
int dp[15][1<<14];
int d[40005],vis[40005];

struct node{
    int di,u;
    bool operator <(const node& rhs) const{
        return di>rhs.di;
    }
};

void spfa(int st,int num){
    int i;
    priority_queue<node> q;
    for(i=0;i<n*m;i++) d[i]=inf;
    d[st]=0;
    memset(vis,0,sizeof(vis));
    q.push((node){0,st});
    while(!q.empty()){
        node p=q.top();
        q.pop();
        int u=p.u;
        if(vis[u]) continue;
        vis[u]=1;
        int x,y,xx,yy,xu;
        x=u/m,y=u%m;
        if(ma[x][y]>-1) dis[num][ma[x][y]]=d[u];
        if(x==0||x==n-1||y==0||y==m-1) c[num]=min(c[num],d[u]);
        for(i=0;i<4;i++){
            xx=x+dir[i][0];
            yy=y+dir[i][1];
            if(s[xx][yy]==-1) continue;
            if(xx>n-1||xx<0||yy>m-1||yy<0) continue;
            xu=xx*m+yy;
            if(d[xu]>d[u]+s[xx][yy]){
                d[xu]=d[u]+s[xx][yy];
                q.push((node){d[xu],xu});
            }
        }
    }
}

int main(){
    int i,j,k,K,x,y,state;
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
         for(j=0;j<m;j++)
           scanf("%d",&s[i][j]);
        memset(ma,-1,sizeof(ma));
        memset(dp,0x7f,sizeof(dp));
        memset(dis,0,sizeof(dis));
        scanf("%d",&K);
        for(i=0;i<K;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            ma[a][b]=i;
            f[i]=a*m+b;
            c[i]=inf;
        }
        for(i=0;i<K;i++) spfa(f[i],i);
        for(i=0;i<K;i++){
            x=f[i]/m,y=f[i]%m;
            dp[i][1<<i]=c[i]+s[x][y];
        }
        for(i=1;i<(1<<K);i++){
        for(j=0;j<K;j++){
            for(k=0;k<K;k++){
                if((i&(1<<j))&&(i&(1<<k))&&j!=k){
                    if(dp[j][i^(1<<k)]!=inf) dp[k][i] = min(dp[k][i], dp[j][i^(1<<k)]+dis[j][k]);
                }
            }
        }
    }
        int ans=inf;
        for(i=0;i<K;i++)
          ans=min(ans,dp[i][(1<<K)-1]+c[i]);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值