HDU 4568 Hunter

题目大意: 自己选择位置进入迷宫,选最好的路径访问所有的宝藏,然后走出迷宫。要求最短”时间“
首先,这是一个迷宫问题。但是普通的dfs,bfs 并不能求出访问所有宝藏的时间总和。
所以,可以再抽象一下,求出宝藏两两间的距离当成边,宝藏当成顶点,然后构图。最短路? 不,最短路不能求和。
尝试dp ,那种dp ? (1<=k<=13 )状态压缩?
想想以前做过的经典题——TSP 旅行商问题。
当是 旅行商问题 求解的是访问所有节点一次,然后回到原点??
这里,并不要求,访问完所有宝藏之后回到第一个访问的宝藏。
但是,进入迷宫和走出迷宫也是需要考虑的。
idea——-干脆再加入一个顶点,编号为0,代表迷宫外。
然后就构成旅行商问题的裸体的。

算法实现:
1 构图 : 枚举每个顶点 i,求出这个顶点距离地图外的最短时间,记录为i到0号顶点的距离。 求出宝藏两两之间的距离。 以邻接矩阵形式储存。
2 跑一遍状态压缩dp 。

另外: 传说旅行商问题有 O(n*(2^n))的写法,但是不知为何wrong 了。。下面给出挑战上 O(n*n*(2^n))的写法.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int n,m,k;
int isin[205][205];
bool inque[205][205];
int xx[15],yy[15];    //珍宝的位置 
int map1[202][202];     //地图上的权值 
int d[202][202];        //广搜的距离数组 
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; //广搜的方向 
const int INF=200*200*10*200;  
typedef pair<int ,int > P;
int dist[15][15];
int dp[1<<15][15];
int   bfs(int sx,int sy,int oo)
{
    memset(inque,0,sizeof(inque));
   queue<P> que;
   while(!que.empty())
   {    que.pop();  
   } 
   for(int i=0;i<=n;i++) 
   for(int j=0;j<=m;j++) 
        d[i][j]=INF;

    d[sx][sy]=0;
    inque[sx][sy]=true;
    que.push(P(sx,sy));
     int mi=INF;
    while(!que.empty())
   {
       P p=que.front(); que.pop();
       inque[p.first][p.second]=false;
       for(int i=0;i<4;i++)
       {
         int nx=p.first +dir[i][0],ny=p.second +dir[i][1];
         if(nx>=0&&nx<n && ny>=0&&ny<m)
         {    
            if(map1[nx][ny]!=-1 &&d[p.first ][p.second ]+map1[nx][ny]<d[nx][ny])
            { 
                 if(isin[nx][ny]>0) 
               {         
                 if(d[p.first ][p.second ]<dist[isin[nx][ny]][oo])
                  {
                  dist[oo][isin[nx][ny]]= dist[isin[nx][ny]][oo]=d[p.first ][p.second ];

                  } 
               }              
                d[nx][ny]=d[p.first ][p.second]+map1[nx][ny];     
                if(inque[nx][ny]==false)
                {
                que.push(P(nx,ny)); 
                 inque[nx][ny]=true;
                }
            }   
         }
          else 
          { 
              if(d[p.first ][p.second]<mi) 
              mi=d[p.first ][p.second];
          } 
       } 
   }
   return mi;
}

// wrong answer 的写法。 
 /*void solve()    
{ 
    k=k+1;
    int num1=1<<k;
    for(int s=0;s<num1;s++)
    {
       fill(dp[s],dp[s]+k,INF); 
    }

    for(int i=0;i<k;i++)
    {
        dp[1<<i][i]=dist[i][0];
    }

    int num3=1<<k;
     for(int s=0;s<num3;s++){        
          for(int i=0;i<k;i++){
                  if(s&(1<<i)==0)continue;
                  if(dp[s][i]==INF)continue;
                 for(int j=0;j<k;j++){
                     if(s&(1<<j)==1)continue;
                     dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+dist[i][j]);
                 }
              }
          }

    int res=dp[(1<<k)-1][0];
    for(int i=1;i<=k-1;i++)
    {
        res+=map1[xx[i]][yy[i]];
    }
    printf("%d\n",res);
}  */
 /* int rec(int s,int v)
 {
    if(dp[s][v]>=0) return dp[s][v];

    if(s==(1<<k)-1&&v==0)
    {
        return dp[s][v]=0;
    }
     int res=INF;
     for(int u=0;u<k;u++)
     {
         if(!(s>>u&1))
         {
            res=min(res,rec(s|1<<u,u)+dist[v][u]);
         }
     }
     return dp[s][v]=res; 
 }*/
void solve1()
{
        k=k+1;
    int num1=1<<k;
    for(int s=0;s<num1;s++)
    {
       fill(dp[s],dp[s]+k,INF); 
    }
    //  printf("%d %d\n",dist[1][0],dist[0][1]);
     dp[(1<<k)-1][0]=0;
    for(int s=(1<<k)-2;s>=0;s--)
    {
        for(int v=0;v<k;v++)
        {
            for(int u=0;u<k;u++)
            { 
               if(!(s>>u&1))
               {
                  dp[s][v]=min(dp[s][v],dp[s|1<<u][u]+dist[v][u]);
               } 
            } 
        }
    } 
      int res=dp[0][0];
    for(int i=1;i<=k-1;i++)
    {
        res+=map1[xx[i]][yy[i]];
    }
    printf("%d\n",res);
}
int main()
{

  //    freopen("F:\\123.txt","r",stdin);
     int t;
     scanf("%d",&t);
     for(int ik=1;ik<=t;ik++)
     {  
       memset(isin,0,sizeof(isin));
       scanf("%d%d",&n,&m);

       for(int i=0;i<=15;i++)
       for(int j=0;j<=15;j++)
       dist[i][j]=INF;

        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
           scanf("%d",&map1[i][j]);     
            } 
        }   
           scanf("%d",&k); 
           for(int i=1;i<=k;i++)
           {
              scanf("%d%d",&xx[i],&yy[i]);
              isin[xx[i]][yy[i]]=i;
           }
           int sx,sy,gx,gy;
          for(int i=1;i<=k;i++)
          {
           dist[i][0]= dist[0][i]=bfs(xx[i],yy[i],i);

          } 

               solve1();
/*    k=k+1;
    int num=1<<k;
    for(int i=0;i<num;i++)
    {
        for(int j=0;j<20;j++)
        {
            dp[i][j]=-1;
        }
    }

    int res=rec(0,0);
    for(int i=1;i<=k-1;i++)
    {
        res+=map1[xx[i]][yy[i]];
    }
    printf("%d\n",res); */ 
    }
     return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值