附上带中文的题目
这个题的题意就是求S和A组成的最小生成树,这题的一个对大部分人来说的一个坑点是读入,我一开始也是被卡了,发现算出来的答案贼大,然后找bug发现读入都没对,后来又用的gets,然后又发现少读了1行,原因是因为在第一行输入2个数字后会有一个回车,这个回车会占一个gets,所以要先把回车给读了再去读字符。然后就是我自己思路的原因了,太菜了QAQ,我的思路出现了问题,我一开始的思路是2重循环,分别算出一个点到其他所有点的距离,把他们都加入到了求Kruskal的结构体中,代码如下:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <map> #include <queue> const int N =251000; using namespace std; int p[N]; map<int,map<int,int> >mp; char g[100][100]; int x,y; pair<int,int>a[N]; struct node { int u,v,dis; }edge[N]; bool cmp(struct node a,struct node b) { return a.dis<b.dis; } bool vis[100][100]; int d[100][100]; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; int bfs(int sx,int sy,int ex,int ey) { queue<pair<int,int> >q; memset(vis,false,sizeof vis); for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) d[i][j]=0x3f3f3f3f; q.push({sx,sy}); d[sx][sy]=0; vis[sx][sy]=true; while(q.size()) { pair<int,int>t=q.front(); q.pop(); int x1=t.first,y1=t.second; for(int i=0;i<4;i++) { int tx=x1+dx[i],ty=y1+dy[i]; if(tx<1||tx>x||ty<1||ty>y||g[tx][ty]=='#'||vis[tx][ty]) continue; q.push({tx,ty}); vis[tx][ty]=true; d[tx][ty]=d[x1][y1]+1; if(tx==ex&&ty==ey) return d[tx][ty]; } } } int find(int x) { if(x!=p[x]) p[x]=find(p[x]); return p[x]; } int main() { int t; cin>>t; while(t--) { cin>>y>>x; mp.clear(); gets(g[0]); for(int i=1;i<=x;i++) gets(g[i]+1); int idx=0; for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) { if(g[i][j]=='A'||g[i][j]=='S') { mp[i][j]=++idx; a[idx].first=i; a[idx].second=j; } } for(int i=1;i<=idx;i++) p[i]=i; int cnt=0; for(int i=1;i<=idx;i++) for(int j=i+1;j<=idx;j++) { edge[++cnt].u=i; edge[cnt].v=j; edge[cnt].dis=bfs(a[i].first,a[i].second,a[j].first,a[j].second); } long long ans=0; sort(edge+1,edge+1+cnt,cmp); for(int i=1;i<=cnt;i++) { int u=edge[i].u,v=edge[i].v; int fu=find(u),fv=find(v); if(fu!=fv) { p[fu]=fv; ans+=edge[i].dis; } } cout<<ans<<endl; } return 0; }
这样做是不对滴,为啥呢?如果从一个点(A或S)不能走到其他的A或S,那么在我们求最小生成树的时候就会算上一条根本就不应该存在的边,这条边很大,而且它一定会加在最后的答案里,因为这些边的2个端点在之前一定没有用并查集归为一类,所以就会顺利WA掉。
正解:遍历整个字符矩阵,如果当前点是A或S,那就以该点为起点bfs一遍,一点遇到一个点是S或A,就将其加入到求Kruskal的结构体中,最后排序,累加答案啥的,都是非常正宗的最小生成树算法了。
AC代码:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <map> #include <queue> const int N =251000; using namespace std; int p[N]; map<int,map<int,int> >mp; char g[100][100]; int x,y,cnt; pair<int,int>a[N]; struct node { int u,v,dis; }edge[N]; bool cmp(struct node a,struct node b) { return a.dis<b.dis; } bool vis[100][100]; int d[100][100]; int dx[]={0,0,1,-1},dy[]={1,-1,0,0}; void bfs(int sx,int sy) { queue<pair<int,int> >q; memset(vis,false,sizeof vis); for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) d[i][j]=0x3f3f3f3f; q.push({sx,sy}); d[sx][sy]=0; vis[sx][sy]=true; while(q.size()) { pair<int,int>t=q.front(); q.pop(); int x1=t.first,y1=t.second; for(int i=0;i<4;i++) { int tx=x1+dx[i],ty=y1+dy[i]; if(tx<1||tx>x||ty<1||ty>y||g[tx][ty]=='#'||vis[tx][ty]) continue; q.push({tx,ty}); vis[tx][ty]=true; d[tx][ty]=d[x1][y1]+1; if(g[tx][ty]=='A'||g[tx][ty]=='S') { edge[++cnt].u=mp[sx][sy]; edge[cnt].v=mp[tx][ty]; edge[cnt].dis=d[tx][ty]; } } } } int find(int x) { if(x!=p[x]) p[x]=find(p[x]); return p[x]; } int main() { int t; cin>>t; while(t--) { cin>>y>>x; mp.clear(); gets(g[0]); for(int i=1;i<=x;i++) gets(g[i]+1); int idx=0; for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) { if(g[i][j]=='A'||g[i][j]=='S') { mp[i][j]=++idx; a[idx].first=i; a[idx].second=j; } } for(int i=1;i<=idx;i++) p[i]=i; cnt=0; for(int i=1;i<=x;i++) for(int j=1;j<=y;j++) { if(g[i][j]=='A'||g[i][j]=='S') bfs(i,j); } long long ans=0; sort(edge+1,edge+1+cnt,cmp); for(int i=1;i<=cnt;i++) { int u=edge[i].u,v=edge[i].v; int fu=find(u),fv=find(v); if(fu!=fv) { p[fu]=fv; ans+=edge[i].dis; } } cout<<ans<<endl; } return 0; }
——谨以此博客纪念找bug的一个晚上