FZU 2186 BFS+状压(类似TSP)

点击打开链接

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int M = 110;
typedef struct{
	int x;
	int y;
	int t;
}node;
node move[4]={{-1,0},{1,0},{0,-1},{0,1}};
int n,m,map[M][M];
node mark[15];//记录有宝藏的地方 
int vis[M][M],dis[15][15];// 宝藏之间的最短距离 
int b[15];//b[i] =2^i
int dp[1<<15][15];//dp[s][v] 当前在v结点 && 达到状态为s所需要的时间 
void bfs(int k)
{	
	memset(vis,0,sizeof(vis));
	queue<node> q;
	node s;
	s.x=mark[k].x;
	s.y=mark[k].y;
	s.t=0;
	vis[s.x][s.y]=1;
	q.push(s);
	while(!q.empty())
	{	
	
		node p=q.front();
		q.pop();
		for(int i=0;i<4;i++)
		{			
			int a=p.x+move[i].x;
			int b=p.y+move[i].y;
			int t=p.t+1;
			if(a>=0&&a<n&&b>=0&&b<m&&!vis[a][b])
			{
				if(map[a][b]!=-1)
				{
					vis[a][b]=1;
					node tmp;
					tmp.x=a;
					tmp.y=b;
					tmp.t=t;
					q.push(tmp);
					if(map[a][b]>0)
					{
					
						dis[k][map[a][b]]=t;            //  起点编号为4 dis[k][4]就会有出错 本来是求某个节点k到宝藏4 可能变成求k到起点的距离 
						dis[map[a][b]][k]=dis[k][map[a][b]];// 好坑阿:起点0的map值如果大于0 可能与某个宝藏的编号会重复 
					//	cout<<map[a][b]<<" "<<dis[k][map[a][b]]<<endl;	
					}				
				}			
			}
		}			
	}	
}
int main()
{
	b[0]=1;
	for(int i=1;i<=12;i++)
	{
		b[i]=b[i-1]*2;
	}
	while(cin>>n>>m)
	{
		memset(dis,inf,sizeof(dis));
		int cnt=0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				cin>>map[i][j];
				if(map[i][j]>0&&(i+j!=0))//起点算在mark[0]中 
				{
					mark[++cnt].x=i;
					mark[cnt].y=j;
					map[i][j]=cnt; //与价值无关 可以把价值换成宝藏的编号 
				
				}
			}	
		}
		if(cnt==0&&map[0][0]>0)
		{
			cout<<0<<endl;
			continue;
		}
	
		if(map[0][0]<0)
		{
			cout<<-1<<endl;
			continue;
		}	
		mark[0].x=0;
		mark[0].y=0;// 
		if(map[0][0]>0)// 好坑阿:起点0的map值如果大于0 可能与某个宝藏的编号会重复 
		map[0][0]=99;
		for(int i=0;i<=cnt;i++)//求每个结点间的最短距离 
		{
			bfs(i);
		}
		int flag=1;
		for(int i=1;i<=cnt;i++)
		{		
			if(dis[0][i]==inf)//取不完 
			{	
				cout<<-1<<endl;
				flag=0;
				break;	
			}	
		}
		if(!flag) continue;
				
		memset(dp,inf,sizeof(dp));
		
		
		int goal=b[cnt+1]-1;//目标状态 总共是包括起点在内的  cnt+1位二进制 
		dp[1][0]=0;	
		
		for(int s=1;s<=goal;s++)
		{
			for(int i=0;i<=cnt;i++) //从i到j 
			{
				for(int j=0;j<=cnt;j++)
				{
					if(i!=j)
					{
						if(!(b[i]&s)) //i没加入s 
						continue;
						if(b[j]&s) 
						continue; //j已经加入s 
						
						if(dp[s][i]!=inf) //从小状态推到大 
						dp[s|b[j]][j]=min(dp[s|b[j]][j],dp[s][i]+dis[i][j]);
						 
						// 区别于tsp :dis[i][j]可能会经过原来走过的宝藏点 
						//s|b[j] j加入状态s 
					
					
					}
				}
				
			}
		}
		int ans=inf;
		for(int i=1;i<=cnt;i++)
		{
			ans=min(ans,dp[goal][i]+dis[i][0]); //都走完+回到起点 
		}
		cout<<ans<<endl;
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值