2021 年第十三届四川省 ACM-ICPC 大学生程序设计竞赛

Spicy Restaurant(多元BFS)4星

题意:

给你一个 n n n个点 m m m条边的无向图,每个点有一个权值 w w w q q q次询问问从点 a a a出发走到权值小于等于 b b b的点的最小距离,不能满足输出-1

1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105

1 ≤ w i , b i ≤ 100 1\leq w_i,b_i\leq 100 1wi,bi100

1 ≤ a i ≤ n 1\leq a_i\leq n 1ain

题解:

因为权值的范围比较小,所以我们可以按照权值跑BFS记录每种情况每个点到权值 w w w的最小距离

注意题目要求小于等于,所以最后还需要更新一下最小值

#include<bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e5+10,M=2*N;
int h[N],e[M],ne[M],idx;
int g[N],dist[110][N];
int q[N];
int n,m,k;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int u){
    for(int i=1;i<=n;i++) dist[u][i]=-1;
    int hh=0,tt=-1;
    for(int i=1;i<=n;i++){
        if(g[i]==u){
            dist[u][i]=0;
            q[++tt]=i;
        }
    }
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];~i;i=ne[i]){
            int j=e[i];
            if(dist[u][j]!=-1) continue;
            dist[u][j]=dist[u][t]+1;
            q[++tt]=j;
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m>>k;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>g[i];
    while(m--){
        int a,b; cin>>a>>b;
        add(a,b),add(b,a);
    }
    for(int i=1;i<=100;i++) bfs(i);
    for(int i=1;i<=n;i++){
        for(int j=2;j<=100;j++){
            if(dist[j][i]==-1) dist[j][i]=dist[j-1][i];
            else if(dist[j-1][i]!=-1) dist[j][i]=min(dist[j][i],dist[j-1][i]);
        }
    }
    while(k--){
        int a,b; cin>>a>>b;
        cout<<dist[b][a]<<endl;
    }
    return 0;
}

Don’t Really Like How The Story Ends(贪心+dfs)4星

题意:

已知一个 n n n个点 m m m条边的无向图,问最少加多少条边才能使从1出发dfs得到1~n的一个序列

题解:

设当前点为 x x x,且 x x x的上一个点为 y y y

  • 当x直接与 x + 1 x+1 x+1相连时,可以直接走到 x + 1 x+1 x+1
  • x x x不与 x + 1 x+1 x+1直接相连
    • x x x不与大于 x + 1 x+1 x+1的点相连时,倘若 y y y x + 1 x+1 x+1相连则不需要加边,若 y y y也不与 x + 1 x+1 x+1相连则在 y y y加边也不会使当前答案更差,所以我们可以选择回溯到结点 y y y进行判断
    • x x x与大于 x + 1 x+1 x+1的点 t t t相连时,必须加一条边
    • 上述情况可能有 t > > x + 1 t>>x+1 t>>x+1,需要多次重复加边知道 n o w = t now=t now=t
#include<bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PII;
const int N=1e5+10;
int n,m,ans,now;
vector<int>ve[N];
void dfs(int u){
    if(u>n) return ;
    for(auto p:ve[u]){
        if(p<now) continue;
        while(p>=now){//上述情况可能有$t>>x+1$,需要多次重复加边知道now=t
            if(p==now) now++,dfs(now-1);
            else now++,ans++,dfs(now-1);
        }
    } 
}
void solve(){
    cin>>n>>m;
    ans=0,now=2;//直接从2开始判断
    for(int i=1;i<=n;i++) ve[i].clear();
    while(m--){
        int a,b; cin>>a>>b;
        if(a>b) swap(a,b);
        ve[a].push_back(b);
    }
    ve[1].push_back(n+1);//加一条到n+1的边作为边界
    for(int i=1;i<=n;i++) sort(ve[i].begin(),ve[i].end());
    dfs(1);
    cout<<ans<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值