河南萌新联赛2024第(一)场 week 2 DAY 3

事后诸葛亮,脑子不好使了

A-造数_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

题意:给定一个整数 𝑛n ,你可以进行以下三种操作:+1   , +2        ,*2,问至少多少次操作可以将0转为n;

题解:对于一个数,如果它是奇数就减一,如果是偶数,就除以二,知道n等于0为止。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200005],op=0;
signed main(){
	int n;
	cin>>n;
	while(n){
		if(n%2==0)n/=2;
		else {
			n--;
		}
		op++;
        if(n==1)break;
      //  cout<<n<<endl;
	}
	cout<<op<<endl;
	return 0;
}

B-爱探险的朵拉_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

其实我到现在还不知道为什么会超时,我觉得应该不会,我的思路是遍历每一个未访问的点,如果一个点是没有被访问的,就记录它的循环周期是多长,顺便把这个周期上的所有点标记为已访问。。。。。我觉得时间复杂度是O(n)但是T了,如果有谁知道可以告诉我嘛  —_—;

只是我错误的代码(TLE):

#include<bits/stdc++.h>
using namespace std;
int p[100005];
#define int long long
int ans=0;
int vis[100005];
signed main(){
int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p[i];
    }
    for(int i=1;i<=n;i++){
        int be=i;
        int cur=i;
        vis[i]=1;
        int num=1;
        cur=p[cur];
        if(vis[cur]==0){
            
             while(cur!=be){
                 vis[cur]=1;
                num++;
            
            cur=p[cur];
        }
        ans=max(ans,num);
        }
       
    }
cout<<ans<<endl;
    return 0;
}

正确的做法:拓扑排序(半年前的知识点的了,记不得了T-T)

拓展:(拓扑排序):入度为0的结点依次删掉排序。

拓扑排序可以判断有向图中是否有环。可以生成拓扑序列;若序列中的元素个数等于n,则有拓扑序,否则有环

算法的核心:用队列维护一个入度为0的节点的集合。

1,初始化,队列q压入所有入度为0的点;

2,每次从q中取出一个点x放入数组tp;

3,然后将x的所有出边删除,若将边(x,y)删除后,y的入度变为0,则将y压入q中;

4,不断重复2,3,过程,知道队列q为空;

这题难度有点大,晚点补

C-有大家喜欢的零食吗_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

题意,每个小朋友都有自己喜欢的n种零食,要让每个小朋友都得到互不相同的零食,求最少还需要买多少零食?

二部图最大匹配:匈利亚算法:

(有权):领接矩阵,给每一行减去其最小值,使每一行至少有一个0;然后给每一列减去最小值,使每一列至少有一个0,。

然后要做循环:

1.用尽量少的线覆盖所有的0;

2,决定是否终止循环。假如用的线条数等于n(节点数),就终止循环,如果小于n就继续循环

3,在矩阵中产生更多的0;,在所有未被覆盖的元素中找到最小的值,记为k

,然后把所有为被覆盖的元素都减去k;还需要把k加到红线交叉的地方。

结束循环后,n个0代表图中的n条边,每个0对应一条边,要在n个0里边选择m(顶点数)代表最大小匹配,最大匹配操作只需要将数据取反即可;

(无权):通过不停地找增广路来增加匹配边。找不到增广路时,达到最大匹配。可以用DFS或者BFS来实现。

算法:

链上前向星(存边):

就是链接表:

 

struct E
{
	int to;
	int w;
	int next;
}Edge[maxm];

for(int i=Head[u];~i/*(i==-1)指向空终止循环*/;i=Edge[i].next){
		int v=Edge[i].to;
		int w=Edge[i].w;
	}
void AddEdge(int u,int  v,int w){
	//前面三句是创造一个空结点,给新节点进行初始化
	Edge[tot].to=v;
	Edge[tot].w=w;
	Edge[tot].next=Head[u];//将空结点指向所连接的Head[u];
	Head[u]=tot++;
}

 以上内容是链式向前星的内容。

无权最大匹配;

流程:

边e[i] 存第i条出边{v,ne}

表头h[u]:存u点的第一条出边

标记vis[v]:存女生v是否访问

配对match[v]:存女生v的男友;、

匈牙利算法:男女相亲,男选女可占可让,贪心配对:

1.枚举n个男生,

每轮vis[]初始化为0,(即女生皆可选),

深搜若能配成对,ans+1;

2.枚举男生u的心仪女孩v,

(1)若女孩已标记就逃过

(2)若女孩没男朋友,则配成对

若女孩的男友可以让出,则配成队。

(3)否则,枚举u的下一个心仪女孩

3.枚举完u的心仪女孩都不能配成对,则return 0;

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,m,a,b,ans;
#define M 10000
#define N 200005
struct edge{
	int v,next;
}e[M];
int h[N],idx;
int vis[N],match[N];
void add(int a,int b){
	e[++idx]={b,h[a]};
	h[a]=idx;
}
bool dfs(int u){
	for(int i=h[u];i;i=e[i].next){
		int v=e[i].v;//枚举该男孩的心动女孩
		if(vis[v])continue;
		vis[v]=1;//先标记这个女孩
		if(!match[v]/*如果该女孩没有对象*/||/*枚举此女孩的男友还有没有可选的女孩*/dfs(match[v])){
			match[v]=u;//配成对
			return 1;
		}
	}
	return 0;
}
signed main(){
	cin>>n>>m>>k;
	for(int i=0;i<k;i++){
		cin>>a>>b;
		add(a,b);
	}
	for(int i=1;i<=n;i++){//男选女,枚举每一个男生
		memset(vis,0,sizeof(vis));
		if(dfs(i))ans++;//走进男生i;
	}
	cout<<ans<<endl;
}

题解: 此题不知到为什么用链式向前星会超时,可能用错了,因为对他还是不太熟悉,所以此题用邻接图来做;储存一个邻接表,遍历每一个孩子,然后遍历每一个孩子喜欢的零食,如果该零食没有被选择,则这个零食就和该孩子匹配,或者说,如果该零食被选择,就查找持有该零食的孩子是否还有其他零食可以选择,如果有,则持有该零食的孩子就把零食让给现在所遍历到的孩子,然后绑定零食和孩子之间的关系。最后,如果每个孩子都有自己的零食,就输出:Yes,否者输出n-ans;

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,m,a,b,ans;
#define M 10000
#define N 200005

int vis[505],match[505];
int e[505][505];
bool dfs(int u){
	for(int i=1;i<=n;i++){
		if(!vis[i]&&e[u][i]){
			vis[i]=1;
			int t=match[i];//小孩所喜欢的零食中,查找没被匹配的,以及查找有无可以替换的;
			if(t==-1||dfs(t)){
				match[i]=u;//第i个零食是小孩u的
				return 1;
			}
		}
	}
	return 0;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>m;
		
		for(int j=1;j<=m;j++){
			cin>>b;
			e[i][b]=1;
		}
	}
		ans=0;
		memset(match,-1,sizeof(match));
		for(int i=1;i<=n;i++){//孩子选零食
			memset(vis,0,sizeof(vis));
			if(dfs(i))ans++;//走进i;
		}
	

if(ans==n)cout<<"Yes"<<endl;
else cout<<"No"<<endl<<n-ans<<endl;
return 0;
}

D-小蓝的二进制询问_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

 我的队友最强大脑!!!;

找到从l到r的二进制数下1的个数

0000000
0000001
0000010
0000011
0000100
0000101
0000110
0000111
0001000
0001001
0001010
0001011
0001100
0001101
0001110
0001111
0010000

通过观察可以发现,从右向左开始,第

第一列隔一行01(2)第二列是0011(4),第三列式000111(6),第四列是00001111(8);

我们要计算 l 到 r 的二进制下1的个数,就用1到 r 的1的个数减去1到 l 的1的个数;

我们只需要将 l  r 转换成二进制看看二进制下长度是多少,说明我们循环多少次累加1;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 998244353
int f(int x){
	int sum=0;
	int p2=1;
	for(;p2<=x;p2*=2){//p2是一个01组合中,0或则1的个数
		int j=x/(2*p2);//2*p2,代表是一个周期,j表示x行中有几个周期;
		sum+=j*p2;//sum加上这一行中的1的个数等于周期数*周期数中1的个数
		int y=x%p2-p2/2;//计算没有在周期上,1的个数,因为0在前边,所以x%p2是剩余的数,p2/2是0的个数;
		if(y>0)sum+=y;//如果减掉前面0的个数以后值>0,就说明还有1可以加1
		sum%=N;
	}
	sum+=x-(p2)/2;//若x在16到32之间,x为25,当p2为16时也就是计算完第三列后就退出循环,还剩下最后一列没有将一算进去‘
	
	sum%=N;
	return sum;
}
signed main()
{
	ios::sync_with_stdio(false);cin.tie(nullptr);
	int t;
	cin >> t;
	while(t--){
		int l,r;
		cin >> l >> r;
		cout << (f(r+1)-f(l)+N)%N<< endl;
	}
	return 0;
}
/*0000000
  0000001
  0000010
  0000011
  0000100
  0000101
  
  0000110
  0000111
  0001000
  0001001
  0001010
  0001011
  0001100
  0001101
  0001110
  0001111
  0010000*/

TIRED!!!!!

G-旅途的终点_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

二分找到答案就行

#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<int>a(200005);
int n,m,k;
bool check(int x){
	int mm=m;
	auto p=a;
	sort(p.begin()+1,p.begin()+x+1,greater<int>());
	
	for(int i=k+1;i<=x;++i){
		
	}
	return 1;
	
}
signed main(){
	cin>>n>>m>>k;
   
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
    int ans=0;
	int l=0,r=n;
	while(l<=r){
		int mid=(l+r)/2;
		if(check(mid)){
			
			l=mid+1;
			ans=mid;
		}
		else r=mid-1;
	}
	cout<<ans<<endl;
	return 0;
}

H-两难抉择_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

很显然乘一个最大值(n)是最好的选择,但是要注意,若n==1或则数组中所有的数都为1,那么加就是最好选择

#include<bits/stdc++.h>
using namespace std;
#define int long long
int minn=0;
signed main(){
    int n;
    cin>>n;
    int ans=0;
    for(int i=1;i<=n;i++){
        int c;
        cin>>c;
        if(c>minn){
            minn=c;
        }
        ans+=c;
}
    
    if(n==1)ans+=1;
    else if(minn==1)ans+=n;
    else{
        ans-=minn;
    ans+=minn*n;
    
    }
    cout<<ans<<endl;
    return 0;
}

I-除法移位_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

要使得结果最大必须保证a1尽可能的大,则就需要找出最后k个数中最大的那个数,如果没有第一个数大,就不移动,否则,就移动n-最大元素的下标+1步;

#include<bits/stdc++.h>
using namespace std;
#define int long long
int pos=0,maxx=0;
int a[200005];
map<int,int>mp;
signed main(){
	int n,t;
	cin>>n>>t;
    int one;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
    one=a[1];
	for(int i=n;i>=1;i--){
		mp.insert(make_pair(a[i],i));
	}
	if(n>t)sort(a+n-t+1,a+1+n);
	else {
		sort(a+1,a+1+n);
	}
   // for(int i=1;i<=n;i++)cout<<a[i]<<' ';
	if(one>=a[n])cout<<0<<endl;
	else cout<<n-mp[a[n]]+1;
	return 0;
}

K-图上计数(Easy)_河南萌新联赛2024第(一)场:河南农业大学 (nowcoder.com)

如果n是偶数,那么除以二,一边一半平方后最大,

如果是奇数,那么除以二后,有一堆会多一1,同样此时也是最大的

#include<bits/stdc++.h>
using namespace std;
#define int long long
int pos=0,maxx=0;
int a[200005];
map<int,int>mp;
signed main(){
	int n,m;
	cin>>n>>m;
	if(n%2==0)cout<<(n/2)*(n/2)<<endl;
	else cout<<(n/2)*((n/2)+1)<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值