搜索
医院设置
题目描述
设有一棵二叉树,如图:

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 $1$。如上图中,若医院建在 $1$ 处,则距离和 $=4+12+2\times20+2\times40=136$;若医院建在 $3$ 处,则距离和 $=4\times2+13+20+40=81$。
## 输入格式
第一行一个整数 $n$,表示树的结点数。
接下来的 $n$ 行每行描述了一个结点的状况,包含三个整数 $w, u, v$,其中 $w$ 为居民人口数,$u$ 为左链接(为 $0$ 表示无链接),$v$ 为右链接(为 $0$ 表示无链接)。
输出格式
一个整数,表示最小距离和。
样例 #1
样例输入 #1
```
5
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0
```
样例输出 #1
```
81
```
提示
数据规模与约定
对于 $100\%$ 的数据,保证 $1 \leq n \leq 100$,$0 \leq u, v \leq n$,$1 \leq w \leq 10^5$。
由于数据范围较小,我一开始使用的暴力做法,利用BFS分层之后再用BFS进行距离统计,然后得出最小值
时间复杂度类似于SPFA,为O(n^2logn)
//https://www.luogu.com.cn/problem/P1364 #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<cmath> #include<queue> #include<map> using namespace std; const int N=550; int n,m,u,v,root,res=0x3f3f3f,num; int e[N],ne[N],h[N],w[N],idx,ans,dist[N]; bool vis[N]; void add(int a,int b) { e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void _bfs(int u) { memset(dist,0x3f3f3f,sizeof dist); queue<int>que; que.push(u),dist[u]=1; while(!que.empty()){ int now=que.front();que.pop(); for(int i=h[now];~i;i=ne[i]){ int j=e[i]; if(dist[j]>dist[now]+1){ dist[j]=dist[now]+1; que.push(j); } } } } int bfs(int u) { queue<int>que; que.push(u),vis[u]=true,num=1,ans=0; while(!que.empty()){ int now=que.front();que.pop(); for(int i=h[now];~i;i=ne[i]){ int j=e[i]; if(!vis[j]) ans+=abs(dist[j]-dist[u])*w[j],que.push(j),vis[j]=true; } } return ans; } int main() { cin>>n; memset(h, -1, sizeof h); for(int i=1;i<=n;i++){ cin>>root>>u>>v; w[i]=root; if(u!=0) add(i,u),add(u,i); if(v!=0) add(i,v),add(v,i); } for(int i=1;i<=n;i++) _bfs(i),memset(vis,false,sizeof vis),res=min(res,bfs(i)); cout<<res; return 0; }
后来无意间看到,如果求的是树上所有的点到一个点的距离最小的话,为什么不能用树的重心呢?很明显这个点就是树的重心
时间复杂度直接降到O(nlogn)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> using namespace std; const int N=1e4+10; int n,m,res,f[N],g[N],num,sum,ans=0x3f3f3f3f; int e[N],ne[N],h[N],w[N],idx,dist[N],cnt[N]; bool vis[N]; void add(int a,int b) { e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void _bfs(int u) { memset(dist,0x3f3f3f,sizeof dist); queue<int>que; que.push(u),dist[u]=1; while(!que.empty()){ int now=que.front();que.pop(); for(int i=h[now];~i;i=ne[i]){ int j=e[i]; if(dist[j]>dist[now]+1){ dist[j]=dist[now]+1; que.push(j); } } } } void dfs(int u) { vis[u]=true; for(int i=h[u];~i;i=ne[i]){ int j=e[i]; if(!vis[j]){ dfs(j); g[u]+=g[j]+w[j]; f[u]=max(f[u],g[j]+w[j]); } } f[u]=max(f[u],sum-g[u]-w[u]); vis[u]=false; } void spfa(int u) { queue<int>que; que.push(u); while(!que.empty()){ int now=que.front();que.pop(); for(int i=h[now];~i;i=ne[i]){ int j=e[i]; if(!vis[j]) res+=abs(dist[j]-dist[u])*w[j],que.push(j),vis[j]=true; } } } int main() { cin>>n; memset(h, -1, sizeof h); for(int i=1;i<=n;i++){ int a,b,c; cin>>c>>a>>b; w[i]=c,sum+=c; if(a!=0) add(a,i),add(i,a); if(b!=0) add(i,b),add(b,i); } dfs(1); for(int i=1;i<=n;i++) if(ans>f[i]) num=i,ans=f[i]; memset(vis,false,sizeof vis); _bfs(num); memset(vis,false,sizeof vis); spfa(num); cout<<res; return 0; }
01迷宫
## 题目描述
有一个仅由数字 $0$ 与 $1$ 组成的 $n \times n$ 格迷宫。若你位于一格 $0$ 上,那么你可以移动到相邻 $4$ 格中的某一格 $1$ 上,同样若你位于一格 $1$ 上,那么你可以移动到相邻 $4$ 格中的某一格 $0$ 上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
## 输入格式
第一行为两个正整数 $n,m$。
下面 $n$ 行,每行 $n$ 个字符,字符只可能是 $0$ 或者 $1$,字符之间没有空格。
接下来 $m$ 行,每行两个用空格分隔的正整数 $i,j$,对应了迷宫中第 $i$ 行第 $j$ 列的一个格子,询问从这一格开始能移动到多少格。
## 输出格式
$m$ 行,对于每个询问输出相应答案。
## 样例 #1
### 样例输入 #1
```
2 2
01
10
1 1
2 2
```
### 样例输出 #1
```
4
4
```
## 提示
所有格子互相可达。
- 对于 $20\%$ 的数据,$n \leq 10$;
- 对于 $40\%$ 的数据,$n \leq 50$;
- 对于 $50\%$ 的数据,$m \leq 5$;
- 对于 $60\%$ 的数据,$n,m \leq 100$;
- 对于 $100\%$ 的数据,$1\le n \leq 1000$,$1\le m \leq 100000$。
首先暴力bfs,最终得分70pts:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<cmath> using namespace std; const int N=1e3+10; int n,m,res,t,k; char mp[N][N]; struct node { int x,y,step; }; bool vis[N][N]; int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; void bfs(int stx,int sty) { queue<node>que; node str; str.x=stx,str.y=sty,str.step=0,vis[stx][sty]=true;; que.push(str); while(!que.empty()){ node now=que.front(); que.pop(); int x=now.x,y=now.y,dis=now.step,num=4; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=dy[i]+y; if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!vis[xx][yy]){ if(mp[x][y]=='0'&&mp[xx][yy]=='1') res++,vis[xx][yy]=true,que.push({xx,yy,dis+1}); if(mp[x][y]=='1'&&mp[xx][yy]=='0') res++,vis[xx][yy]=true,que.push({xx,yy,dis+1}); } } } cout<<res<<endl; } int main() { std::cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j]; while(m--){ int x,y; cin>>x>>y; memset(vis,false,sizeof vis); res=1; bfs(x,y); } return 0; }
考虑优化,如果读一遍遍历一遍肯定是不行了,这里要用到连通块染色的思想,由于题目说任意点都是互通的,所以当我们遍历一个点时,在过程中所有的点的答案都是遍历这个点最终得到的答案,优化成功
下面是dfs和bfs代码:
//dfs #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<cmath> using namespace std; const int N=1e3+10; int n,m,num,res[1000001],g[N][N]; char mp[N][N]; int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; void dfs(int x,int y,int id) { g[x][y]=id;res[id]++; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx<1||xx>n||yy<1||yy>n||mp[xx][yy]==mp[x][y]||g[xx][yy]!=-1) continue; dfs(xx,yy,id); } } int main() { cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j],g[i][j]=-1; while(m--){ int x,y; cin>>x>>y; if(g[x][y]==-1) dfs(x,y,m); cout<<res[g[x][y]]<<endl; } return 0; }
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<cmath> using namespace std; const int N=1e3+10; int n,m,t,k; char mp[N][N]; struct node { int x,y; }; int cnt[N][N],res[100001]; int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; void bfs(int stx,int sty) { cnt[stx][sty]=m,res[m]++; queue<node>que; que.push({stx,sty}); while(!que.empty()){ node now=que.front();que.pop(); for(int i=0;i<4;i++){ int x=now.x+dx[i],y=now.y+dy[i]; if(x>=1&&x<=n&&y>=1&&y<=n&&cnt[x][y]==-1&&mp[x][y]!=mp[now.x][now.y]){ cnt[x][y]=m,res[m]++,que.push({x,y}); } } } } int main() { std::cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>mp[i][j],cnt[i][j]=-1; while(m--){ int x,y; cin>>x>>y; if(cnt[x][y]==-1) bfs(x,y); cout<<res[cnt[x][y]]<<endl; } return 0; }