poj 3026 广搜+最小生成树

本文介绍了一种利用Prim算法求解最小生成树的问题,并通过广度优先搜索计算顶点间的最短路径。针对特定场景(如存在障碍物的地图),文章详细展示了如何构建图并找到连接所有节点的最短总路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*

http://acm.pku.edu.cn/JudgeOnline/problem?id=3026

题目大意:求从集合(刚开始只有S点)出发到所有A,不计算重复路径,只要到达一个A,就把该A点加入到集合中。其实就是求一个包含所有点的最小生成树。

 

算法:把S点和A点当做一样,计算两两之间的最短距离,由于有路障“#”的存在,所以采用广搜进行计算。(本来觉得如果已经计算了i到j,则不用再计算j到i,但事实上用广搜的话,这个步骤是不能省的)。然后就用prim计算最小生成树,就可以了。

*/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<functional>
using namespace std;
const int INF=(1<<31)-1;
const double EPS=1e-8;
const int N=110;
int path[N][N];
char str[N][N];
bool v[N][N];
int n,m;
int num=0;
struct P
{
       int x,y;
}p[N];
int X[3000];
int Y[3000];
int S[3000];
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
int start;


bool check(int x,int y)
{
   if(x<0||x>m||y<0||y>n)return false;    
   if(str[x][y]=='#')return false;
   return true; 
}

int prim(int s)
{
  int ans=0;
  int d[N];
  for(int i=0;i<num;i++)d[i]=INF;
  d[s]=0;
  bool vv[num];
  memset(vv,false,sizeof(vv));
  for(int i=0;i<num;i++)
  {
     int mark=-1;
     int Min=INF;
     for(int j=0;j<num;j++)
      if(!vv[j]&&d[j]<Min){Min=d[j];mark=j;}
     if(mark==-1)return -1;
     vv[mark]=true;
     ans+=Min;
     for(int j=0;j<num;j++)
      if(!vv[j]&&d[j]>path[mark][j])d[j]=path[mark][j];       
  }
  return ans;
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    cin>>T;
    while(T--)
    {
       cin>>n>>m;
       num=0;
       gets(str[0]);
       for(int i=0;i<m;i++)gets(str[i]);
       for(int i=0;i<m;i++)
       {
          int len=strlen(str[i]);
          for(int j=0;j<len;j++)
          {
             if(str[i][j]=='A')
             {
                p[num].x=i;
                p[num++].y=j;                 
             }       
             else if(str[i][j]=='S')
             {
                start=num;
                p[num].x=i;
                p[num++].y=j;
             }
          }
       }         
       for(int i=0;i<num;i++)
        for(int j=0;j<num;j++)
         path[i][j]=INF;
      
       for(int i=0;i<num;i++)//对每个节点i进行广搜
       {
           int front=0,end=1;
           memset(v,false,sizeof(v));
           v[p[i].x][p[i].y]=true;
           X[front]=p[i].x;
           Y[front]=p[i].y;
           //cout<<p[i].x<<" "<<p[i].y<<endl;
           S[front]=0;
           int find=0;
           while(front<end)
           {
               int xx=X[front];
               int yy=Y[front];
               for(int j=0;j<num;j++)
               {
                  if(i!=j&&xx==p[j].x&&yy==p[j].y){path[i][j]=S[front];find++;break;}       
               }
               if(find==num-1)break;
               for(int j=0;j<4;j++)
               {
                  int tx=xx+dx[j];
                  int ty=yy+dy[j];
                  if(check(tx,ty)&&!v[tx][ty])
                  {
                     X[end]=tx;
                     Y[end]=ty;
                     S[end]=S[front]+1;
                     end++;         
                     v[tx][ty]=true;              
                  }    
               }        
               front++;               
           }           
       }
       /*
       for(int i=0;i<num;i++)
       {
        for(int j=0;j<num;j++)
          cout<<path[i][j]<<" ";
        cout<<endl;
       }
         */ 
       cout<<prim(start)<<endl;  
    }
    //system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值