河南萌新联赛2024第(三)场:河南大学

河南萌新联赛2024第(三)场:河南大学

B 正则表达式

x.x.x.x,其中 x表示一个十进制数字, … 没有特殊含义,表示其本身

给出几个这样的序列,x>=0&&x<=255

求满足条件的有多少序列。

思路

我是把字符转为十进制的数。题解上将数字和 “ . ” 分开输入,倒是没想到,挺简单的,不过我的也不复杂,一个循环而已。

代码

signed main()
{
	IOS
	int t;
	cin>>t;
	while(t--)
    {
        string s;
        cin>>s;
        int sum=0,f=0;
        for(int i=0;i<=s.size();i++)
        { 
            if(s[i]=='.'||i==s.size())
            { 
                if(sum>=0&&sum<=255)
                sum=0;
                else{
                   f=1; 
                   sum=0;
                }
            }
            else
                sum=sum*10+s[i]-'0';
       }
       if(f==0)
       ans++;
   }
   cout<<ans<<'\n';
}

C Circle

​ 几个圆相交,最多分成多少区域。

思路

输入

4
0 1 2 3

输出

1 2 4 8

这个是样例,我画了一个四个圆,数的是14,没想那么多,忽然注意到样例,就简单的以为是2的n次方。想着可能我画的图不规范,所以数的不是16。由于前几次萌新的“经验”,果断的提交了,导致了我这场比赛,错的唯一一发。

又仔细看了看图,如果是2^n,又是什么依据呢,怎么会这么大。还是用我的数学判断一下

外部区域 +1

每两个圆应该是都能组成一个新的区域 +n (n-1)

每个圆会有一个公共区域 +1

这样看起来才合理,提交就对了。

因为O很特殊,关键是有半径,自然不可能,想组成一个区域,就组成一个区域,画图也会有偏差,毕竟画的不圆,用数学知识应该能计算出来,题解上的递推,也很合理。当然,这些在我看来只是合理!

代码

void solve()
{
 int n;
 cin>>n;
  int sum=0;
    sum=1+n*(n-1)+1;
    if(n==0) sum=1;
    cout<<sum<<' ';
}
signed main()
{
	IOS
	int t;
	cin>>t;
	while(t--)
	solve();
}

J keillempkill学姐の卷积

​ 也就是两个矩阵也某种方式(卷积),相乘。

思路

题意很好理解,不过怎么用代码更好的实现,对我还是得想一下。

最初我定义了 i, j,从1到m-n+1循环,这样结果就可以放在C[ i ] [ j ]中,之后再想怎么在这个循环中将两个矩阵相乘,并加到C数组中。根据题意,a数组的每个数都与C数组中的数字有关,所以应该在循环中,再开两个循环,来遍历数组 a,而b数组选择数字的变化,可以用这四个循环来控制b数组的选择

这样,四个循环就ok了

代码

signed main()
{
	IOS
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	cin>>a[i][j];
	for(int i=1;i<=m;i++)
	for(int j=1;j<=m;j++)
	cin>>b[i][j];
	for(int i=1;i<=m-n+1;i++)
	for(int j=1;j<=m-n+1;j++)
	{
	     
	    for(int l=0;l<n;l++)
	    for(int k=0;k<n;k++)
	    c[i][j]+=a[l+1][k+1]*b[i+l][j+k];
	}
    for(int i=1;i<=m-n+1;i++)
    {
        for(int j=1;j<=m-n+1;j++)
	  cout<<c[i][j]<<" ";
	    cout<<'\n';
    }
	 
}

L SSH

这是一个模拟题,因为上周也出过。当时一股脑写了100多行,这次动脑子了,写了60多行,没再用结构体,map,vector,实现的。

思路

定义map<string,string> mi 存放私钥和公钥

map<string,map<string,vector > > com; 外层first ,存IPV4地址。内层first,存用户名;second,存公钥

之后进行查询,代码中for,if 套娃,里边放f=1原本的确这样写,麻烦了,一个for就够了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
map<string,string> mi;
map<string,map<string,vector<string> > > com;
signed main()
{
	IOS
	int m,n,q;
	cin>>m>>n>>q;
	string pub,pri;
	for(int i=1;i<=m;i++)
	{
	    cin>>pub>>pri;
	    mi[pri]=pub;
	}
	string ip;
	int k;
	for(int i=1;i<=n;i++)
	{
	    cin>>ip>>k;
	    string user,pub2;
	    int t;
	    for(int j=1;j<=k;j++)
	    {
	        cin>>user>>t;
	        for(int jj=1;jj<=t;jj++)
	        {
				cin>>pub2;
				com[ip][user].push_back(pub2); 
            }
	    }
	}
	string quser,qip,qpri;
	 while(q--)
	 {
	 	int f=0;
	 	cin>>quser>>qip>>qpri;
    	for(auto il:com[qip][quser])
            if(il==mi[qpri]) {
                f=1;
            }
		if(f)
		cout<<"Yes"<<'\n';
		else
		cout<<"No"<<'\n';
	 }
}

F 累加器

给一个数字x,还有Y。x进行y次+1操作。整个过程中x的二进制位数发生了几次变化。

思路

看到这个题,我的第一思路就是第一周的萌新,有一道【D 小蓝的二进制询问】

河南萌新联赛2024第(一)场:河南农业大学_小蓝的二进制询问-CSDN博客

在这个博客中,同样的我们可以选择0做一个参考物。看从0到x会发生几次变化。

0000

0001

0010

0011

0100

0101

0110

可以看出,第一位每个数都在变,第二位每两个数变一次,依次是第N位,每2^(N-1)个数变一次。累加起来就好了。

代码

 int jisuan(int x)
{
    int e=1,sum=0;
    while(x>=e)
    {
        sum=sum+x/e;
        e*=2;
    }
    return sum;
}
void solve()
{
     int x,y;
     cin>>x>>y;
     int ans=jisuan(y+x)-jisuan(x);
     cout<<ans<<'\n';
}
signed main()
{
	IOS
	int t;
	cin>>t;
	while(t--)
	solve();
}

I 游戏

链接

给定 n 个点, m 条边的有权无向图,每个边有状态,0不可通过。在k节点有钥匙,拿到钥匙,可通过。问从1到n的最小花费。

思路

这是个最短路径问题,用dijkstra算法。需要考虑一个额外条件,是否经过k。

所以用dis存放花费时,dis[ i] [0]表示不经过结点k的花费。dis[i] [1]经过节点k的最小花费

先创建一个邻接表存放图的信息

再创建二维数组存到达某节点的最小花费

用优先队列进行Dijkstra,更新节点最小花费

两种情况取最小值即为答案。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define fir(i,a,b) for(int i=a;i<=b;i++)
#define PII pair<int,int> 
#define fi first
#define se second
#define tup tuple<int,int,int>//可包含不同类型  int h=get<0>(t)
void solve()
{
    int n,m,k,a,b,c,d;
    cin>>n>>m>>k;
     vector<vector<tup>> g(n+1);//存边的信息
    vector<vector<int>>  dis(n+1,vector<int>(2,1e11));//定义n+1行两列的数组,初始化1e11
	//用于存储从起点到每个节点的最小花费,dis[i][0]表示不经过结点k的花费。dis[i][1]经过节点k的最短时间
    priority_queue<tup,vector<tup>,greater<tup> >  q;
    //用来存储待处理节点,弹出最小花费
    fir(i,1,m)
    {
       cin>>a>>b>>c>>d;
       g[a].push_back({b,c,d});
       g[b].push_back({a,c,d});
    }
    q.push({0,1,k==1});
    while(!q.empty())
    {
        auto [distance,place,is_ok]=q.top();
        //结构化绑定访问,取出最小花费节点
        q.pop();
        if(dis[place][is_ok]!=1e11) continue;
        //到达该节点的最小花费计算过了,跳过
        dis[place][is_ok]=distance;
        //更新该节点的最小花费
        for(auto [b,c,d]:g[place])
        {
            int is_now=is_ok==1||b==k;//更新记录有没有经过k
            //ok=1,说明经过k,v==k到达钥匙所在节点,判断是否经过K
            if(d||is_ok)
            q.push({distance+c,b,is_now});
            //如果该节点可通过或在此前有钥匙,将新的节点推入
        }
    }
    int ans=min(dis[n][0],dis[n][1]);
    if(ans==1e11) cout<<"-1"<<'\n';
    else
    cout<<ans<<'\n';
}
signed main()
{
    IOS
    int t;
    t=1;
    while(t--)
    solve();
    return 0;
}

E 区间

链接

在一个长度为n的纸带上,初始时所有位置颜色为白色,现在要执行以下两种操作一共q次

​ 操作一:输入一个下标x,你需要将位置x的颜色翻转(白色变为黑色,黑色变为白色)

​ 操作二; 输入两个正整数L, R,你需要输出区间[L,R]中的连续的白色区间长度最大值

思路

看这题的数据,暴力肯定不行。感觉和二分有些关系。(题解上给的线段树,比较复杂,主要是因为我不会😰)。想敲二分没成功,写出来一个纯暴力。

看有人代码用lower_bound() 写的。

定义一个vector,将要翻转的坐标存入,如果存在就删除。这样vector里存放的就是黑块,由于用迭代器查找,插入,所以里面是升序排列。这样当给出L,R.把他们中的黑块处(也就是vector里存放的L,R之间的)计算比较一下就好了。

用set应该也是可以实现的。,原来不行!!!set会超时

vector 是一个动态数组,而set是一个红黑树。

性能差异:set的插入和查找操作的时间复杂度是O(log n),而vector的插入和查找操作的时间复杂度是O(1)。因此,对于大量操作,set可能会比vector慢得多。

排序开销:set内部自动维护元素的有序性,这需要额外的排序开销。对于vector,你可以直接访问任何元素,而不需要进行排序

代码

 void solve()
{
     int n,q,op,L,R,x;
     vector<int> v;
     cin>>n>>q;
     while(q--)
     {
         cin>>op;
         if(op==1)
         {
             cin>>x;
             auto t=lower_bound(v.begin(),v.end(),x);
             if(t!=v.end()&&*t==x)//存在x
             v.erase(t);
             else
             v.insert(t,x);//按升序插入的
         }
         else
         {
             cin>>L>>R;
             auto t=lower_bound(v.begin(),v.end(),L);
             if(t==v.end()||*t>R)
             cout<<R-L+1<<'\n';
             else
             {
                 int k=*t,ans=*t-L;
                 t++;
                 while(t!=v.end()&&*t<=R)
                 {
                     ans=max(ans,*t-k-1);
                     k=*t;
                     t++;
                 }
                 ans=max(ans,R-k);
                 cout<<ans<<'\n';
             }
         }
     }   
}
signed main()
{
    IOS
    int t; 
    t=1;
    while(t--)
    solve();
    return 0;
}

D 开心消消乐(Right Version)

思路

完全没想到D竟然如此简单,过的竟然这么少。早知道就应该看看这个题

由于l是递减的,也就是每次只能从后往前消,相同的一起消除,为零的跳过。

代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int a[1000500];
signed main()
{
    IOS
    int n,ans=0;
    a[0]=-1;
    cin>>n;
    for(int i=1;i<=n;i++)
    { cin>>a[i];
     if(a[i]!=a[i-1])
     {if(a[i]!=0)
         ans++;
       }
     }   cout<<ans<<'\n';
      
}

总结

这次做的挺顺的,不过还是老样子,就会那几道,也就写了那几道。但是D没写出来,真是太可惜了

虽然现在看,E(存黑块),I (dijkstra),也不复杂,但之前的确没想到,不了解。每一次都像在原地踏步…

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值