poj 2688 状态压缩dp解tsp

题意:

裸的tsp。

分析:

用bfs求出任意两点之间的距离后可以暴搜也可以用next_permutation水,但效率肯定不如状压dp。dp[s][u]表示从0出发访问过s集合中的点,目前在点u走过的最短路程。

代码:

//poj 2688
//sep9
#include <iostream>
#include <queue>
using namespace std;
const int maxW=32;
const int maxN=12;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
char graph[maxW][maxW];
int g[maxW][maxW];
int vis[maxW][maxW];
int d[maxN][maxN];
int n,w,h;
int dp[1<<maxN][maxN];

struct Node{
	int x,y;
}dirty_pnt[maxN];

void bfs(int s)
{
	memset(vis,-1,sizeof(vis));		
	queue<Node> Q;
	Q.push(dirty_pnt[s]);
	vis[dirty_pnt[s].x][dirty_pnt[s].y]=0;
	while(!Q.empty()){
		Node q=Q.front();Q.pop();		
		int x=q.x,y=q.y;
		for(int i=0;i<4;++i){
			int nx=x+dx[i];
			int ny=y+dy[i];
			if(nx>=0&&nx<h&&ny>=0&&ny<w&&g[nx][ny]>=0&&vis[nx][ny]==-1){
				vis[nx][ny]=vis[x][y]+1;
				Node p;
				p.x=nx,p.y=ny;
				Q.push(p);
			}
		}
	}
} 

int rec(int s,int u){
	if(dp[s][u]!=-1)
		return dp[s][u];
	int i,j,res=INT_MAX;
	int ss=s&(~(1<<u));
	for(i=0;i<n;++i)
		if(ss>>i&1)
			res=min(res,rec(ss,i)+d[i+1][u+1]);
	return dp[s][u]=res;
}
int main()
{
	int i,j,k;
	while(scanf("%d%d",&w,&h)==2&&(w+h)){
		for(i=0;i<h;++i)
			scanf("%s",graph[i]);
		n=0;
		for(i=0;i<h;++i)
			for(j=0;j<w;++j){
				if(graph[i][j]=='*'){
					g[i][j]=++n;
					dirty_pnt[n].x=i;
					dirty_pnt[n].y=j;
				}
				else if(graph[i][j]=='.')
					g[i][j]=0;		
				else if(graph[i][j]=='o'){
					g[i][j]=0;
					dirty_pnt[0].x=i;
					dirty_pnt[0].y=j;
				}else
					g[i][j]=-1;
			}
		int not_reach=0;
		for(k=0;k<=n&&!not_reach;++k){
			bfs(k);	
			for(i=0;i<=k;++i){
				d[k][i]=d[i][k]=vis[dirty_pnt[i].x][dirty_pnt[i].y];
				if(d[k][i]==-1){
					not_reach=1;
					break;
				}
			}
		}
//		for(i=0;i<=n;++i)
//			for(j=0;j<=n;++j){
//				printf("%d %d==%d\n",i,j,d[i][j]);
//			}
		if(not_reach==1){
			printf("-1\n");
			continue;
		}
		memset(dp,-1,sizeof(dp));
		int ans=INT_MAX;
		for(i=0;i<n;++i)
			dp[1<<i][i]=d[0][i+1];
		for(i=0;i<n;++i)
			ans=min(ans,rec((1<<n)-1,i));
		printf("%d\n",ans);
	}
	return 0;	
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值