搜索

医院设置

 题目描述

设有一棵二叉树,如图:

![](https://cdn.luogu.com.cn/upload/image_hosting/kawht13x.png)

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 $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;
}

 

posted @ 2023-08-17 23:58  o-Sakurajimamai-o  阅读(13)  评论(0编辑  收藏  举报
-- --