重庆城市科技学院第十三届重庆程序设计竞赛选拔赛

重庆城市科技学院第十三届重庆程序设计竞赛选拔赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

A.好数对


链接:https://ac.nowcoder.com/acm/contest/108648/A

给你一个整数数组 a。
如果一组数字 (i,j) 满足 a[i] = a[j] 且 i < j ,就可以认为这是一组 好数对 。

核心思想:如果有两个及以上那么便可以组成好数对,那么直接用map存一下每个数的个数,然后用组合数的公式c(n,2),也就是n*(n-1)/2,然后用ans把所有的加起来就可以过了

void moon()
{
     int n;
     cin>>n;
     map<int,int>mp;
     for(int i=1;i<=n;i++){
         int x;
         cin>>x;
         mp[x]++;
     }
     int ans=0;
     for(auto [y,x]:mp){
         if(x>=2){
             // cout<<x<<'\n';
             ans+=((x-1)*x)/2;
             // cout<<ans<<'\n';
         }
     }
     cout<<ans<<'\n';
}


B.NB Prime


https://ac.nowcoder.com/acm/contest/108648/B

已知正整数 n 是两个**不同的质数的乘积**,试求出两者中较大的那个质数

核心思想:题目直接说明了这个n是由两个质数的乘积,那么我们直接从后往前找到第一个能被整除的数输出即可
```

void moon()
{
     int n;
     cin>>n;
    for(int i=n-1;i>=2;i--){
        if(n%i==0)return cout<<i<<'\n',void();
    }
}


```

 C.迷宫问题


https://ac.nowcoder.com/acm/contest/108648/C


核心思想:这个我们只需要跑两遍dfs(深度优先搜索),或者用bfs(广度优先搜索)跑一边迷宫即可
特别注意这题的数据范围![alt](https://uploadfiles.nowcoder.com/images/20250423/546178801_1745408168198/D2B5CA33BD970F64A6301FA75AE2EB22)

首先我们用vector开一个string数组,这里也是卡了很多人,因为你如果开全局变量的话由可能n=2e5,m=1,也有可能**n=1,m=2e5**

所有这里如果不用vector在内部开数组的话肯定会被卡内存的

还有的人会问在全局开一个vector<string>g[N+1];可以吗,答案是**不行的**
  
 如果你这样开的话会出现一种情况放不下,m=2e5,那么你此时的数组占用就是一个**2e5*2e5=4e10**的内存,这是我们不能接受的
  
**dfs**(深搜):**记得打标记!!!**

 **深搜的思路**:就是先跑一遍dfs,把整个迷宫遍历一遍,如果有出口那么直接输出YES
 如果在搜图的过程中碰见了K(钥匙),那么打个标记ok=1
  
  第二次深搜(主要是为了那种第一次遇见D(门)但是没有钥匙的情况)![alt](https://uploadfiles.nowcoder.com/images/20250423/546178801_1745408581296/D2B5CA33BD970F64A6301FA75AE2EB22)

  那么你此时只能先第一次把钥匙拿了,再跑一边dfs才能成功走到终点

``` 

void moon()
{
      int n,m,ok=0;
      cin>>n>>m;
      vector<string>s(n);
      vector vis(n,vector<int>(m));
      for(int i=0;i<n;i++)cin>>s[i];
      auto dfs=[&](auto self,int x,int y,int &ok)->void{
         if(x==n-1&&y==m-1){
            cout<<"YES"<<endl;
            exit(0);
         }
         for(int i=0;i<4;i++){
            int nx=x+dx[i],ny=y+dy[i];
            if(nx<0||nx>=n||ny<0||ny>=m||s[nx][ny]=='#')continue;
            if(vis[nx][ny])continue;
            if(ok==0&&s[nx][ny]=='D')continue;
            if(s[nx][ny]=='K')ok=1;
            vis[nx][ny]=1;
            self(self,nx,ny,ok);
         }
      };
      dfs(dfs,0,0,ok);
      for(int i=0;i<n;i++){
         for(int j=0;j<m;j++)vis[i][j]=0;
      }
      dfs(dfs,0,0,ok);
      cout<<"NO\n";
}


```
  **bfs**
  
  **广搜的思路**:
  直接bfs跑一遍即可解决,只需要在搜索途中**打标记**,还有如果你在**搜索途中遇见了K**,记得把**标记全部清一遍**,相当于以K这个点的位置又开一次bfs(广搜)
  
``` 
 

void moon()
{
    int n,m;
    cin>>n>>m;
    vector<string>s(n);
    for(int i=0;i<n;i++)cin>>s[i];
    queue<pll>q;
    q.emplace(0,0);
    vector vis(n,vector<bool>(m));
    int ok=0;
    bool flag=true;
    while(q.size()){
      auto [x,y]=q.front();q.pop();
      for(int i=0;i<4;i++){
         int nx=x+dx[i],ny=y+dy[i];
         if(nx<0||nx>=n||ny<0||ny>=m)continue;
         if(s[nx][ny]=='#')continue;
         if(s[nx][ny]=='D'&&ok==0)continue;
         if(s[nx][ny]=='K'&&ok==0){
            for(int i=0;i<n;i++){
               for(int j=0;j<m;j++){
                  vis[i][j]=0;
               }
            }
            ok=1;
         }
         if(vis[nx][ny])continue;
         vis[nx][ny]=1;
         q.emplace(nx,ny);
      }
    }
    cout<<(vis[n-1][m-1]==1?"YES":"NO");
}


```
  


 D.    谁改了我的键位



  **核心思路**:这题也是直接map存一下就过了(**我只能感叹一句STL真好用**),我们先用map把每个字母出现的次数统计一下,然后把这些个数全部放进a数组中(**主要是想用sort**)进行一个从大到小排序
  然后把相邻的两个数相减即可
  
``` 
 

void moon()
{
    string s;
    int n;
    cin>>n>>s;
    map<char,int>mp;
    for(int i=0;i<s.size();i++){
        mp[s[i]]++;
    }
    vector<int>a;
    for(auto [c,x]:mp){
        a.push_back(x);
    }
    sort(a.begin(),a.end(),greater<int>());
    int t=a.size();
    int ans=0;
    if(t==1)ans+=a[0];
    if(t>=2)ans+=a[0]-a[1];
    if(t==3)ans+=a[2];
    if(t>=4)ans+=a[2]-a[3];
    cout<<ans<<'\n';
}


```


 E.    MJ哥哥的质数


  **核心思路**:你只需要注意他是拆解成质因子(也就是所有因数都是质数),那么假设我的m是一个质因子全是2的便是最小的那种情况了,总所周知2是最小的质数,那么全是2组成的数一定是m最小的情况,如果n比这种m还小那就只能输出NO
  
```

  void moon()
{
    int n,ans=0;
    cin>>n;
    int t=n;
    for(int i=2;i*i<=n;i++){
        while(n%i==0)ans++,n/=i;
    }
    if(n!=1)ans++;
    int m=pow(2,ans+1);
    cout<<(m<t?"YES":"NO")<<'\n';
}


```
  
F.丢手绢


  这题你别看他这么大一堆,有用的就一句话,假设你在i这个位置,丢手绢的话你要么丢在i+1,要么就丢在i+2的位置,然后用dp跑一遍就过了
  
  **核心思路**:竟然已经想到用dp了,那么我们需要推一下状态转移方程
  
  首先我们下一次丢手绢的位置只能是**i+1或者i+2**,那么我们此时是由那个状态转移过来的呢???
  
  很显然就是**min(dp[i-1],dp[i-2])+a[i]**,前两个位置中最小的那一个加上丢在当前这个位置的生气值
  
  
``` 
 

 void moon()
{
    int n;
    cin>>n;
    vector<int>a(n+1),dp(n+1);
    for(int i=0;i<n;i++)cin>>a[i];
    dp[0]=a[0],dp[1]=a[1];
    for(int i=2;i<=n;i++){
        dp[i]=a[i]+min(dp[i-1],dp[i-2]);
    }
    cout<<dp[n]<<'\n';
}


```

 G.奈何桥


**核心思路**:拓扑排序加上dp求最短路即可
                  
这题也可以用dijkstra(毕竟这个边权并不为负值)
                  
**拓扑排序加dp求最短路**
                  
首先我们用邻接表把图存下来,然后我们需要把**除了1以外入度为0的点筛掉**(因为我们不筛的话,求的就**不一定是1到x的最短路**了,有可能会求到某个入度为0的点到x的路径),然后判断每次输入的黑边(0)和白边(1)的边权
            
 状态转移方程就是**dp[son]=min(dp[son],dp[fa]+w?b:a);**
                  
 **特别注意** :每次的dp都得重新赋值    

```

void moon()
{
      int n,m;
      cin>>n>>m;
      vector<pll>g[n+1];
      vector<int>in(n+1);
      for(int i=1;i<=m;i++){
         int u,v,w;
         cin>>u>>v>>w;
         g[u].push_back({v,w});
         in[v]++;
      }
      queue<int>q;
      for(int i=2;i<=n;i++)if(in[i]==0)q.emplace(i);
      while(q.size()){
         auto u=q.front();q.pop();
         for(auto [v,w]:g[u]){
            in[v]--;
            if(in[v]==0)q.emplace(v);
         }
      }
      int k;
      cin>>k;
      while(k--){
         int a,b,x;
        cin>>a>>b>>x;
        vector<int>dp(n+1,0x3f3f3f3f);
        vector<int>in1=in;
        dp[1]=0;
        q.emplace(1);
        while(q.size()){
            auto u=q.front();
            q.pop();
            for(auto [v,w]:g[u]){
                in1[v]--;
                if(in1[v]==0)q.emplace(v);
                if(w==0){
                    dp[v]=min(dp[v],dp[u]+a);
                }
                else dp[v]=min(dp[v],dp[u]+b);
            }
        }
        cout<<dp[x]<<'\n';
      }
 
}


```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值