HDU 4568 Hunter

18 篇文章 0 订阅

题目:

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

题意:

n*m的棋盘中放着一些宝藏,每个点都有经过的耗时,要求从任意位置进入棋盘后拿到所有宝藏然后离开棋盘,每个点可以经过多次,求最小耗时,-1的点是不可走的。

思路:

图的大小是200*200但是宝藏最多只有13个,所以很容易想到重新建图,把13各点互相之间的距离求出来建图然后有一个虚拟点就是棋盘外,求出所有点到棋盘外的耗时。然后对这张完全图进行TSP就行了。TSP的部分简单,就是dp状压。主要是建图比较麻烦,要进行13次bfs,也就是spfa队列优化的松弛求最短路。鉴于这图展开的话m<2*n且n<40000保证是稀疏图所以用spfa基本能够保证O(km)的复杂度,每次松弛然后处理新的图f[i][j],最后TSP即可。

代码:

#define N 212
#define M 15
int n,m,k;
int flag,sum,ave,ans,res,ans1,ans2;
int a[N],b[N];
int g[N][N],f[M][M],dist[N][N],len[M];
int dir[4][2]={1,0,-1,0,0,1,0,-1};
int dp[M][1<<14];
bool vis[N][N];
void spfa(int u)
{
    int x,y,w,i;
    memset(dist,0x3f,sizeof(dist));
    memset(vis,false,sizeof(vis));
    queue<pair<int ,int> > q;
    while(!q.empty())q.pop();
    q.push(make_pair(a[u],b[u]));
    dist[a[u]][b[u]] = 0;
    while(!q.empty())
    {
        x = q.front().first; y = q.front().second;  q.pop();
        vis[x][y] = false;
        if(x==0||x==(n-1)||y==0||y==(m-1))
            len[u] = min(len[u],dist[x][y]);
        for(i=0;i<4;i++)
        {
            int xx=x+dir[i][0],yy=y+dir[i][1];
            if(xx>=0&&xx<n&&yy>=0&&yy<m&&g[xx][yy]!=-1&&dist[xx][yy]>dist[x][y]+g[xx][yy])
            {
                dist[xx][yy]=dist[x][y]+g[xx][yy];
                if(!vis[xx][yy])
                {
                    vis[xx][yy]=true;
                    q.push(make_pair(xx,yy));
                }
            }
        }
    }
}
int main()
{
    int i,j,T,cas,t,x,y,z;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
            for(j=0;j<m;j++)
                scanf("%d",&g[i][j]);
        scanf("%d",&k);
        for(i=0;i<k;i++)
            scanf("%d%d",&a[i],&b[i]);
        memset(f,0x3f,sizeof(f));
        for(i=0;i<k;i++)f[i][i]=0;
        memset(len,0x3f,sizeof(len));
        memset(dp,0x3f,sizeof(dp));
        for(i=0;i<k;i++)
        {
            spfa(i);
            for(j=0;j<k;j++)
                f[i][j] = min(f[i][j],dist[a[j]][b[j]]);
            dp[i][1<<i] = len[i]+g[a[i]][b[i]];
        }
        for(j=0;j<(1<<k);j++)
            for(i=0;i<k;i++)
                if(j&(1<<i)&&dp[i][j]!=INF)
                    for(x=0;x<k;x++)
                        if(!(j&(1<<x)))
                            dp[x][j|(1<<x)]=min(dp[x][j|(1<<x)],dp[i][j]+f[i][x]);
        res=INF;
        for(i=0;i<k;i++)
            res=min(res,dp[i][(1<<k)-1]+len[i]);
        printf("%d\n",res);
    }
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值