题目链接:点击查看
题意:n*n的图,给出m个隧道的起点和终点,问经过所有隧道所需要的最小时间,在隧道内不花时间,每个隧道只能走一次
题解:先预处理出每个隧道的终点到其他隧道的起点的最小距离,因为隧道只有15个,然后状压一下就可以了,主要要记录一个隧道的终点到其他的起点,因为起点或终点有可能会有一样的,我刚开始记录成终点到终点了,但是对于终点一样,起点不一样的隧道来说就会有多个值,就没法搞了,但是我们记录到起点的就可以了,即使终点不一样,那么花费的时间也是一样的。然后就是状压了 dp[i][j] 表示以i隧道最后一个走,经过了j状态的最小距离
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
char mp[18][18];
int dis[18][(1<<15)+10],id[18][18],vis[18][18];
int n,m;
struct P{
int x1,x2,y1,y2;
}a[18];
int flag;
int nex[4][2]={0,1,1,0,0,-1,-1,0};
struct node{
int x,y,step,pos;
node(){}
node(int x_,int y_,int step_)
{
x=x_;y=y_;step=step_;;
}
};
int num[18][18][18][18];
void init()
{
memset(num,INF,sizeof(num));
memset(dis,INF,sizeof(dis));
memset(id,0,sizeof(id));
}
void DIJ(int x,int y)
{
memset(vis,0,sizeof(vis));
queue<node> q;
node now;
int xx,yy;
int ll;
q.push(node(x,y,0));
vis[x][y]=1;
while(!q.empty())
{
now=q.front();q.pop();
if(id[now.x][now.y])
{
num[x][y][now.x][now.y]=now.step;
}
for(int i=0;i<4;i++)
{
xx=now.x+nex[i][0];
yy=now.y+nex[i][1];
if(xx<=0||xx>n||yy<=0||yy>n) continue;
if(vis[xx][yy] || mp[xx][yy]=='#') continue;
vis[xx][yy]=1;
q.push(node(xx,yy,now.step+1));
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]+1);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
id[a[i].x1][a[i].y1]=1;
}
for(int i=1;i<=m;i++)
{
DIJ(a[i].x2,a[i].y2);
}
int mm=1<<m;
int pos;
for(int i=1;i<mm;i++)
{
for(int j=1;j<=m;j++)
{
if((1<<(j-1)) & i)
{
pos=i^(1<<(j-1));
if(pos==0) dis[j][i]=0;
for(int k=1;k<=m;k++)
{
if((1<<(k-1)) & pos)
{
dis[j][i]=min(dis[j][i],dis[k][pos]+num[a[k].x2][a[k].y2][a[j].x1][a[j].y1]);
}
}
}
}
}
int ans=INF;
for(int i=1;i<=m;i++) ans=min(ans,dis[i][mm-1]);
if(ans==INF) ans=-1;
printf("%d\n",ans);//
}
return 0;
}
/*
5 4
.....
####.
.####
####.
.####
1 1 1 4
2 5 3 1
3 1 4 5
4 5 5 1
5 4
#...#
.###.
.....
.....
.....
1 2 1 4
1 3 2 1
2 5 2 1
2 1 3 1
*/