hdu 4856 Tunnels 哈密顿通路

点击打开链接


题意: 给了n*n 的地图,M条单向隧道,问最小时间访问所有隧道一遍;如果不可以输出-1;

先用dfs 找出 某一隧道的出口 到 其他隧道入口的 dis ; dfs 时,用vis[st.x][st.y][i][j] 去标记从 起点st  到(i,j) 的最小距离,当搜索是当前的dep >= vis,即可剪枝,不必再搜!

哈密顿用状态压缩DP :dp[x][i] : x 表访问的状态; i 表示此状态最后一个访问的节点号;

dp[0001][1] 表示当前只访问过1号,最后一次访问时1 ; 如果此时又1到2号的道路 ,那么状态即可转移为  dp[0011][2] = min(,dp[0001][1]+dist[1][2]);

状态转移方程为: dp[x][i] <--- dp[x-(1<<i)][j];

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<list>
#include<map>
#include<set>
using namespace std;
const int M=18;
char pos[M][M];
struct node{
   int x,y;
};
struct P{
   node st,ed;
   void init(){
      scanf("%d%d%d%d",&st.x,&st.y,&ed.x,&ed.y);
      st.x--;st.y--;ed.x--;ed.y--;
    }

}a[M];
int n,m;
int vis[M][M][M][M],dist[M][M];
int dp[1<<16][16];

node st;
bool check(int &x,int y){
   if(x!=-1 && x<=y) return 1;
    x=y; return 0;
}
void dfs(int x,int y,int dep){
   if(x<0 || x>=n || y<0 || y>=n ||pos[x][y]=='#') return ;
   if(check(vis[st.x][st.y][x][y],dep)) return ;
//    printf("vis[%d][%d][%d][%d]= %d\n",st.x,st.y,x,y,vis[st.x][st.y][x][y]);
   dfs(x+1,y,dep+1);
   dfs(x-1,y,dep+1);
   dfs(x,y+1,dep+1);
   dfs(x,y-1,dep+1);
}
void deal_min(int &x,int y){
   if(y==-1) return ;
   else if(x==-1) x=y;
   else x=min(x,y);
}
int main()
{

//     freopen("in.in","r",stdin);
     while(~scanf("%d%d",&n,&m)){
         for(int i=0;i<n;i++)
           scanf("%s",pos[i]);
         for(int i=0;i<m;i++)
            a[i].init();

         memset(vis,-1,sizeof(vis));
         memset(dist,-1,sizeof(dist));
         for(int i=0;i<m;i++){
           st=a[i].ed;
           dfs(st.x,st.y,0);
           for(int j=0;j<m;j++)if(j!=i){
                 dist[i][j]=vis[st.x][st.y][a[j].st.x][a[j].st.y];
           }
         }

//        for(int i=0;i<m;i++)
//            for(int j=0;j<m;j++)
//              printf("dist[%d][%d]= %d\n",i,j,dist[i][j]);

         memset(dp,-1,sizeof(dp));
         for(int i=0;i<m;i++) dp[1<<i][i]=0;

         for(int x=1;x<(1<<m);x++){
            for(int i=0;i<m;i++) if((x>>i)&1){
                 for(int j=0;j<m;j++) if(i!=j && ((x>>j)&1)){
                    if(dist[j][i]!=-1 && dp[x-(1<<i)][j]!=-1)
                       deal_min(dp[x][i], dp[x-(1<<i)][j]+dist[j][i]);
                 }
            }
         }
         int ans=-1,x=(1<<m)-1;
         for(int i=0;i<m;i++){
            deal_min(ans,dp[x][i]);
         }
         printf("%d\n",ans);

     }
     return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值