2023牛客寒假算法基础集训营1题解 8/13

更好的观看体验

已写:A、C、D、G、H、K、L、M

A - World Final? World Cup! (I)

题目链接World Final? World Cup! (I)
算法标签:【模拟】
思路:根据题目大意,进行模拟。
需注意特殊数据:1000000000、0100000000、0101000000答案为10、9、7
代码

#include <iostream>

using namespace std;

void solve()
{
	string str;
	cin >> str;
	
	str = "?" + str;
	
	int x = 0,y = 0;//分别表示当前本队进入的球数
	int cntx = 5,cnty = 5;//分别表示本队还剩余的次数
	
	for(int i = 1;i <= 10;i ++)
	{
		if( i%2 ) //奇数位的时候,为x队进球 
		{
			cntx --;
			if(str[i] == '1')
				x ++;
		}
		if( !(i%2) )//偶数位的时候,为y队进球 
		{
			cnty --;
			if(str[i] == '1')
				y ++;
		}
		
		if((x > y+cnty) || (y > x+cntx) ) 
		//判断 如果一队现有的分数大于另一队现有的分数与还剩的次数和,那么第一队获胜,直接输出 
		{
			printf("%d\n",i);
			return ;	
		}
	}
	
	printf("-1\n");
}

int main()
{
	int T;
	cin >> T;
	
	while(T --)
		solve();
		
	return 0;
}

B - World Final? World Cup! (II)

题目链接World Final? World Cup! (II)
留坑

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

题目链接现在是,学术时间 (I)
算法标签:【贪心】【排序】【模拟】
思路1@eyuhaobanga贪心做法。每个教授发自己的论文,当该论文的引用量为1是,那么ans++。
代码1

#include <iostream>

using namespace std;

const int N = 1e5+10;
int a[N];

void solve()
{
	int n;
	cin >> n;
	
	int ans = 0;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
		if(a[i] > 0) //每个教授发自己的论文,当该论文的引用量为 1 时,那么ans++。
			ans ++;
	}
	
	cout << ans << endl;
}

int main()
{
	int T;
	cin >> T;
	
	while(T --)
		solve();
	
	return 0;
}

思路2排序+模拟做法。先将教授按照所发表的论文的引用量从小到大进行排序,然后从头开始遍历,如果引用量为0的话,直接跳过;当引用量为1时,ans++;当引用量大于1时,假设此时引用量为x,在第i个位置,如果没有超出现有教授的论文即if(i+x-1<n) ans+=x-1,如果超出了,剩下的自己发表自己的论文。
代码2

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5+10;
int a[N];

void solve()
{
	int n;
	cin >> n;
	
	for(int i = 1;i <= n;i ++)
		cin >> a[i];
	
	sort( a+1 , a+n+1 );
	
	int ans = 0;
	for(int i = 1;i <= n;i ++)
	{
		if(a[i] == 0) //如果引用量为0的话,直接跳过;
			continue;
		if(a[i] == 1) // 当引用量为1时,答案+1,表示该教授发表自己的论文 
		{
			ans ++;
			continue;
		}
		
		int cnt = a[i] - 1; // 第i个教授需要发表cnt篇论文,并且每篇引用量都大于cnt
		
		if(i + cnt > n) // 第i个教授找不到cnt篇论文,剩下教授发表自己的论文,答案+剩下的教授数量 
		{
			ans += n - i + 1; 
			break;
		}
		else // 第i个教授可以找到cnt篇论文,答案+论文至少的引用量,i跳转到还没发表论文的位置 
		{
			ans += cnt; 
			i += cnt - 1; 
		}		
	}
	
	printf("%d\n",ans);
}

int main()
{
	int T;
	cin >> T;
	
	while(T --)
		solve();
	
	return 0;
}

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

题目链接现在是,学术时间 (II)
算法标签:【计算几何】
思路
我们可以根据给出的(x,y),将整个坐标划分为四个区域。
image
当(xp,yp)位于不同的区域中时,会有不同的情况。

  • (xp,yp)在第一区域时,需要计算出(xp,yp)与A,B,C,D四个不同的点构成的矩形进行计算
  • (xp,yp)在第二区域时,(xp,yp)与A,B两点可以构成最符合题目的矩形
  • (xp,yp)在第三区域时,(xp,yp)与**(0,0)点**可以构成最符合题目的矩形
  • (xp,yp)在第四区域时,(xp,yp)与A,D两点可以构成最符合题目的矩形

代码

#include <iostream>

using namespace std;

typedef long long ll;

ll x , y , xp , yp;

void solve()
{
	scanf("%lld%lld%lld%lld",&x,&y,&xp,&yp);
	double res = 0;
	
	if(xp <= x && yp <= y) // 在第一区域
	{
		res = max(xp * yp / (double) (x * y) , // (xp,yp)与 A(0,0) 构成的矩形 
		          xp * (y - yp) / (double) (x * y) ); // (xp,yp)与 D(0,y) 构成的矩形
		          
		res = max(res , (x - xp) * yp / (double) (x * y) );// (xp,yp)与 B(x,0) 构成的矩形
		
		res = max(res , (x - xp) * (y - yp) / double(x * y) );// (xp,yp)与 C(x,y) 构成的矩形
		
		printf("%.10f\n",res);
		return ;
	}
	
	if(xp <= x) // 在第二区域 
	{
		res = max(xp * y / (double)(x * y + xp * (yp - y)) , // (xp,yp)与 A(0,0) 构成的矩形 
		         (x - xp) * y / (double)(x * y + (x - xp) * (yp - y)));// (xp,yp)与 B(x,0) 构成的矩形
		printf("%.10f\n",res);
		
		return ;
	}
	
	if(yp <= y) // 第四区域 
	{
		res = max(x * yp / (double)(x * y + yp * (xp - x)) , // (xp,yp)与 A(0,0) 构成的矩形 
				  x * (y - yp) / (double)(x * y + (y - yp)*(xp - x)));// (xp,yp)与 D(0,y) 构成的矩形
		printf("%.10f\n",res);
		
		return ;
	}
	
	// 第三区域
	res = x * y / (double) (xp * yp); // (xp,yp)与 A(0,0) 构成的矩形 
	printf("%.10f\n",res);
}

int main()
{
	int T;
	cin >> T;
	
	while(T --)
		solve();
	
	return 0;
}

E - 鸡算几何

题目链接鸡算几何
留坑

F - 鸡玩炸蛋人

题目链接鸡玩炸蛋人
留坑

G - 鸡格线

题目链接鸡格线
算法标签:【stl】【线段树】【并查集】
思路@FriedChicken:有这样的一个性质:f(x)经过不多次数的操作会收敛到一个不变的值f(x) = x,此时x有三个,分别为0、99、100
image

代码

#include <iostream>
#include <cmath>
#include <set>

using namespace std;

typedef long long ll;

const int N = 100010;

ll n , m , a[N] , ans;
set<int> st;

ll fun(ll x)
{
	return round(sqrt(x)*10);
}

int main()
{
	scanf("%lld%lld",&n,&m);
	
	for(int i = 1;i <= n;i ++)
	{
		scanf("%lld",&a[i]);
		ans += a[i];
		
		if(fun(a[i]) != a[i])
			st.insert(i);
	}
	
	st.insert( n+1 );
	
	int op , l , r , k;
	
	for(int i = 1;i <= m;i ++)
	{
		scanf("%d",&op);
		if(op == 1)
		{
			scanf("%d%d%d",&l,&r,&k);
			int pos = l;
			
			while(true)
			{
				int next = (*st.lower_bound(pos));
				if(next > r)
					break;
				
				for(int j = 1;j <= min(k,20);j ++)
				{
					ans -= a[next];
					a[next] = fun(a[next]);
					ans += a[next];
				}
				
				if(fun(a[next]) == a[next])
					st.erase(next);
				
				pos = next + 1;
			}
		}
		else
			printf("%lld\n",ans);
	}
	
	return 0;
}

H - 本题主要考察了DFS

题目链接本题主要考察了DFS
算法标签:【枚举】
思路:对于一个拼图来说,总共凸起和凹下的数量相同。因此可以遍历计算出最后一片拼图是否有凸起和凹下,也可以都存在。最后需要求的是制作成本,凹下的成本-1,凸起成本+1,原成本为10。
代码

#include <iostream>

using namespace std;

void solve()
{
	int n;
	cin >> n;
	
	string str;
	int cnt = 0; // cnt碰到凹下+1,碰到凸起-1 
	
	for(int i = 1;i <= n*n-1;i ++)
	{
		cin >> str;
		for(int j = 0;j < 4;j ++)
		{
			if(str[j] == '1')
				cnt ++;
			else if(str[j] == '2')
				cnt --;
		}
	}
	
	printf("%d\n",10 + cnt);
}

int main()
{
	int T;
	cin >> T;
	
	while(T --)
		solve();
	
	return 0;
}

I - 本题也主要考察了DFS

题目链接本题也主要考察了DFS
留坑

J - 本题竟也主要考察了DFS

题目链接本题竟也主要考察了DFS
留坑

K - 本题主要考察了dp

题目链接 本题主要考察了dp
算法标签:【构造】【枚举】
思路:根据题意和样例,构造出坏区间总数最少的字符串,在1的个数和0的个数够用的情况下,两个1中间包含两个0,1001;当1或0的个数不够时,字符串的剩下位置全放1或0。构造出字符串后,遍历找出坏区间的数。
代码

#include <iostream>

using namespace std;

int main()
{
	int n , cnt1 , cnt0;//cnt1为1的个数,cnt0为0的个数 
	cin >> n >> cnt1;
	
	cnt0 = n - cnt1;
	
	string str = "?";
	
	for(int i = 1;i <= n;i ++)
	{
		if(cnt1) // 如果1的个数还有的话,字符串末尾加1 
		{
			str += '1';
			cnt1 --;
		}
		if(cnt0)// 如果0的个数还有的话,字符串末尾加0
		{
			str += '0';
			cnt0 --;
			
			if(cnt0) // 为了构造坏区间最少的字符串,两个1中间包含两个0,即1001,判断是否能放第二个0 
			{
				str += '0';
				cnt0 --;
				i ++;
			}
		}
	}
	
	int ans = 0; 
	
	//计算坏区间的数量 
	for(int i = 1;i <= n - 2;i ++)
	{
		int l = i,r = i + 2;
		
		int cnt = 0;
		
		for(int j = l;j <= r;j ++)  
			cnt += str[j] - '0';
			
		if(cnt > 1)
			ans ++;
	}
	
	printf("%d\n",ans);
	 
	return 0;
}

L - 本题主要考察了运气

题目链接本题主要考察了运气
算法标签:【数论(概率论)】
思路@eyuhaobanga:
问团队和问个人是独立的:

  • 问团队:问一次、问两次、问三次、问四次、问四次
    期望:(1+2+3+4+4)/5 = 2.8
  • 问个人:问一次、问两次、问三次、问三次
    期望:(1+2+3+3)/4 = 2.25

2.8+2.28=4.05 选项:32

代码

#include <iostream>

using namespace std;

int main()
{
	printf("32\n");
	return 0;
}

m - 本题主要考察了找规律

题目链接 本题主要考察了找规律
算法标签:【动态规划】
思路dp[i][j]表示已经给i个人分了j仙贝,所收获的最大好感度。

  • 转移方程:image
    枚举第i个人分到的仙贝数k

答案就是:dp数组的最大值。
代码

#include <iostream>

using namespace std;

int n ,m;
double dp[510][510];

int main()
{
	cin >> n >> m;
	
	for(int i = 1;i <= n;i ++)
		for(int j = 0;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)) );

	double ans = 0;
	for(int i = 1;i <= n;i ++)
		for(int j = 0;j <= m;j ++)
			ans = max(ans , dp[i][j]);
		
	printf("%.9lf",ans);
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值