首页 > 编程语言 >2023牛客寒假算法基础集训营1 个人题解(ACDHKL)

2023牛客寒假算法基础集训营1 个人题解(ACDHKL)

时间:2023-01-24 17:22:45浏览次数:65  
标签:cout int 题解 场次 牛客 数字 ans 集训营 题意

A.World Final? World Cup! (I)

题意:

给10场比赛的点球输赢情况,奇数为A队点球,偶数为B队点球

思路:

用两个变量x,y来分别存A队当前赢的场次和B队当前赢的场次

然后就就扫一遍01字符串,如果为当前为奇数且为'1',x++,当前为偶数且为'1',y++

这里最主要的是判断是否当前可以直接终结比赛(后面比赛打不打没有意义)

相当于就算后面的场次全输且对面的队伍全赢也可以赢的局面就可以终结比赛

每次都判断一下是否A队当前赢的场次 > B队赢的场次+后面B队剩余的场次

或者B队当前赢的场次 > A队赢的场次+后面A队剩余的场次

这里算后面的场次的时候我WA了一发

因为我默认都搞成向上取整了(

正确的方法如下:

算A队后面的场次:$(n - i) / 2$

算B队后面的场次:$(n - i + 1) / 2$

最后遍历完了还算不出来就输出-1

代码:

void solve()
{
	string s1;
	cin >> s1;
	n = s1.size();
	int x = 0,y = 0;
	s1 = " " + s1;
	for(int i = 1;i <= n;i++)
	{
		if((i&1)&&(s1[i] == '1')) x++;
		else if(s1[i] == '1') y++;
		if(x > y + (n - i + 1) / 2||y > x + (n - i) / 2)
		{
			cout << i << endl;
			return;
		}
	}
	cout << -1 << endl;
}

总结:

在向上取整的时候一定要考虑奇偶,大概率会不一样(

C.现在是,学术时间 (I)

题意:

给n个数字随意分配到n个位置上

定义每一个位置的价值为(位置上的数大小 > 位置上的数字数量)的最大数量

求经过分配后的每个位置价值之和的最大值

思路:

最开始的思路是贪心

每个数的承载量就是数字的大小

比如一个3就可以和另外两个3放在一个位置上有一个为3的贡献

然后就用map处理一下

然后就喜提WA

原因是没去考虑最大贡献,最大贡献其实就是n

因为把数字叠在一起并不能增大总值,反而有可能降低总值

所以最优策略就是不分配,然后算一下>0的值即可

代码:

void solve()
{
	int ans = 0;
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> temp;
		if(temp >= 1) ans++;
	}
	cout << ans << endl;
}

 总结:

贪心之前想想哪样会更优,算算理论最优值是多少

前面用map做法就是典型的负面贪心

D.现在是,学术时间 (II)

题意:

给一个矩形由$(a,b)$和$(0,0)$构成

再给一个点$(x,y)$,要你找个点和这个点再构成一个矩形

要求这两个矩形的交集面积 / 并集面积最大

思路:

肯定是交集面积越大,并集面积越小越好

然后根据两个点的大小关系我觉得可以分为以下四种情况来讨论

1.x >= a&&y >= b

这种情况很好判断,无脑选原点就完事了

交集为:$a * b$

并集为:$x * y$

2.x <= a&&y <= b

点在第一个矩形里面

显然选第一个矩形的四个点分别讨论一下取交集面积最大的即可

那么交集面积为:$max({x * y,(a - x) * y,x * (b - y),(a - x) * (b - y)})$

并集面积为:$a * b$

3.x >= a&&y <= b

这种情况就比较容易错

因为并集不是$x * y$或者$a * b$了,而且这种情况里又分为两种情况,每个情况的并集不一样

情况1:以原点和(x,y)画第二个矩形

交集:$a * y$

并集:$a * b + (x - a) * y$

情况2:以(0,b)和(x,y)画第二个矩形

交集:$a * (b - y)$

并集:$a * b + (x - a) * (b - y)$

4.x <= a&&y >= b

与第三种情况对称即可,详细见代码

代码:

void solve()
{
	cout<<setiosflags(ios::fixed)<<setprecision(9);
	double ans = 0;
	int a,b,x,y,op,uu;
	cin >> a >> b >> x >> y;
	if(x >= a&&y >= b)
	{
		op = x * y,uu = a * b;
		ans = max(ans,uu * 1.0 / (op * 1.0));
	}
	else if(x <= a&&y <= b)
	{
		op = a * b;
		uu = max({x * y,(a - x) * y,x * (b - y),(a - x) * (b - y)});
		ans = max(ans,uu * 1.0 / (op * 1.0));
	}
	else if(x >= a&&y <= b) 
	{
		op = a * b + (x - a) * y;
		uu = a * y;
		ans = max(ans,uu * 1.0 / (op * 1.0));
		op = a * b + (x - a) * (b - y);
		uu = a * (b - y);
		ans = max(ans,uu * 1.0 / (op * 1.0));
	} 
	else if(x <= a&&y >= b)
	{
		op = a * b + (y - b) * x;
		uu = b * x;
		ans = max(ans,uu * 1.0 / (op * 1.0));
		op = a * b + (y - b) * (a - x);
		uu = b * (a - x);
		ans = max(ans,uu * 1.0 / (op * 1.0));
	}
	cout << ans << endl;
}

总结:

这种分类讨论题一定要画图,而且画图不要太潦草(

之前画图画潦草了迟迟分类不出来,最后重新画了一张就完事了

G.鸡格线

题意:

给一个长为n的数组,每次有两种操作

1:对l到r的数字进行$f(x) = round(10 * \sqrt{x})$

2:求所有数字之和

思路:

第一眼看起来是线段树,也确实能用线段树写(

但是这题还用不着线段树(

打个表看下规律,发现所有>0and<=99的数都会向99靠拢

所有>99的数都会向100靠拢

最大的数操作次数不超过20次就会变成目标数字

那么我们按照题意模拟即可

但是我们不知道哪些数是已经被操作完了的,所以这里有个小技巧

就是用set来存没有完成的数字

然后每次二分找比l大的数去操作$min(20,k)$次,并且更新相关数据即可

注意这里要存下标,不能存数字,因为是对l,r进行操作

每次操作完一个数字看是否还需要操作,不需要就删去这个数字

代码:

int f(int x)
{
	return round(10 * sqrt(x));
}

void solve()
{
	int sum = 0;
	set<int> s;
	cin >> n >> m;
	vector<int> a(n + 1);
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		sum += a[i];
		if(a[i] != f(a[i])) s.insert(i);
	}
	int l,r,k;
	while(m--)
	{
		cin >> temp;
		if(temp == 1)
		{
			cin >> l >> r >> k;
			while(1)
			{
				auto it = s.lower_bound(l);
				int x = *it;
				if(it == s.end()||x > r) break;
				for(int i = 1;i <= min(20ll,k);i++)
				{
					sum += f(a[x]) - a[x];
					a[x] = f(a[x]);
				}
				if(a[x] == f(a[x])) s.erase(it);
				l = x + 1;
			}
		}
		else cout << sum << endl;
	}
}

总结:

看到有这种函数就尝试一下打表,一般都能出规律

对于一些数字只操作有限次,就可以用set存下标配合二分去寻找下一个有效值

H.本题主要考察了DFS

题意:

给$n * n - 1$块积木,然后问缺了哪一块,输出缺的那一块的形状数字

思路:

这里被套了,一看题目有dfs,然后长得也很像个dfs

写dfs写到一半突然发现好像不用dfs

因为最后题目问的缺的那一块并没有具体问是啥形状

只要知道有几个凹的和几个凸的的就行了

那这题就很简单了,统计一下出现的1和2的个数

因为1和2肯定是一一对应的,所以看哪个缺就补哪个就完事了

代码:

void solve()
{
	int x = 0,y = 0,z = 0;
	string s1;
	cin >> n;
	char c;
	for(int i = 1;i <= n * n - 1;i++)
	{
		for(int j = 1;j <= 4;j++)
		{
			cin >> c;
			if(c == '0') x++;
			else if(c == '1') y++;
			else z++;
		}
	}
	if(y < z) cout << 10 - abs(y - z) << endl;
	else if(y == z) cout << 10 << endl;
	else if(y > z) cout << 10 + abs(y - z) << endl;
	// cout << x << " " << y << " " << z << endl;
}

总结:

一定要先看题目问的啥,不要脑补题目条件(

还有看到题目有很明显的知识点大概率是误导(

K.本题主要考察了dp

题意:

要你构造一个长度为n的01串,并且其中1的数量为m

问构造出这样的串中所有长度为3的子区间中1的个数>0的个数的子区间数最少有几个

思路:

由于不想让1的个数>0的个数,考虑用2个0去匹配1个1

很容易这样构造:100100100100....1111

就是先构造上面的100100100这样看剩下几个0和几个1

把1全部放在最后面就是最小值

答案为1的个数-1

但是这样要注意边界问题

如果1的个数为0就会 出现负数,所以要对0取max

另外如果整个串全是1,输出就是n - 1,但是答案是$n - 2$

所以要再对$n - 2$取个min

代码:

void solve()
{
	cin >> n >> m;
	int x = n - m,y = m;
	// cout << x << " " << y << endl;
	while(x >= 2&&y >= 1)
	{
		x -= 2;
		y--;
	}
	cout << min(max(y - 1,0ll),n - 2) << endl;
}

总结:

有时候猜出构造方法了还需要注意边界问题,最好交题前测测极限数据

M.本题主要考察了找规律

题意:

有n个人,m个物品来分配,每次分配得到的贡献值为给这个人分配的物品数 / 未给这个人分配前的物品总数

问获得的贡献值最大为多少

思路:

看范围,好了是dp

状态表示为:前i个人给了j个物品的最大贡献值

状态转移:i - 1个人就给了j - k个物品 + 本次贡献值

就是$dp[i][j] = dp[ i - 1][j - k] + k / m - j + k$

枚举$[0,k]$次即可

然后就出来了

代码:

void solve()
{
	cout<<setiosflags(ios::fixed)<<setprecision(9);
	cin >> n >> m;
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			for(int k = 0;k <= j;k++)
			{
				dp[i][j] = max(dp[i][j],dp[i - 1][j - k] + k * 1.0 / (m - j + k));
			}
		}
	}
	cout << dp[n][m] << endl;
}

总结:

最基础的概率dp

dp还是练的太少太少了,平时遇到dp就跳过((

找个时间专项训练一下(

 

标签:cout,int,题解,场次,牛客,数字,ans,集训营,题意
From: https://www.cnblogs.com/rickly233/p/17056410.html

相关文章

  • CodeForces-907#B 题解
    正文设数组\(c_{x,y,i,j}\)代表\((x,y)\)位置的大格子中\((i,j)\)位置的小格子。很显然,输入中字符的输入顺序是要调整的,实际的顺序是\(x,i,y,j\)。对于输入的\(......
  • 题解
    前言只对SubTask2的选手看过来!!!很好的一道模拟题。坑点分析题目里说的很明白了:只要有\(\ge1\)个带有注释的,就是一定是祖宗人,哪怕在后面或者前面出现过符合乐子人......
  • P3802 小魔女帕琪 题解【期望dp】
    题目传送门P3802解题思路本题的解题思路关键在于分段。每一个结构段的概率在之后的结构段依然适用。判断是否符合这种特性最好方法是随机截取一段观察是否成立发现成......
  • 洛谷P3654 First Step题解
    这是一道暴力枚举。 大致题意:R行C列的棋盘要放下长度为K的线段,“#”表示无法放置,问有多少种放置方法。直接贴代码:#include<bits/stdc++.h>usingnamespacestd;i......
  • P4022 [CTSC2012]熟悉的文章 题解
    题目链接简要题意给定\(m\)个模板串和\(n\)个匹配串,如果一个字符串是一个模板串的子串且长度不小于\(L\)则称其为“熟悉的”,对于每个匹配串,求一个最大的\(L\),满足......
  • 2023牛客寒假基础训练营3 I(哥德巴赫猜想)
    I.灵魂碎片的收集题目大意:定义S(n)表示为所有小于n的约数之和。例如S(10)=1+2+5=8现在给定一个数x,求是否有一个n满足S(n)=x。(题目保证如果x为偶数,那么x-......
  • 程序员经典问题解答
    帮助在学习、上班的过程中,你是否经常遇到疑难问题无法解决,为此备受折磨?别担心,小编精选多道程序员最头痛的技术问题予以回答。QA小伙伴程序大牛C语言 Q:如何引用一个已经定义......
  • Solution 题解 UVA1389 Hard Life: 最小割,有向图,分数规划,和牛顿迭代
    题解UVA1389HardLife:最小割,有向图,分数规划,和牛顿迭代Preface黑题好耶看到了题解里面大多数是二分,我就来讲一讲简单又快速的DinkelbachAlgorithm吧!0-1分数规划......
  • 洛谷P2036 PERKET题解
     先来审题,主要有以下几个条件:酸度求乘积,苦度求和,两者相减的值最小(当然是绝对值)。下面附上AC代码:#include<bits/stdc++.h>//万能头文件usingnamespacestd;//......
  • 【题解】CF193D Two Segments
    题意给定一个\(1\simN\)的排列,在这个排列中选出两段互不重叠的区间,求使选出的元素排序后构成公差为1的等差数列的方案数。选出的两段区间中元素构成的集合相同时视为同一......