2020牛客暑期多校训练营(第三场) BAC补题报告

L签到

B:已知一个字符串s,下列有q个操作,按题意的操作输出。

模拟肯定会超时,没有发现规律的我自闭。
将字符串看成一个首尾相连的环。
由于操作的时候,字符串的顺序并没有发生变化,所以,我们可以定义一个字符串的起始地址。
每一次操作,只改变起始地址即可。
本题的时间卡得比较死,cout也会超时,白白送了好几发t。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int main()
{
	string s;
    cin >> s;
	int q;
	scanf("%d",&q);
	int start = 0;
	for(int i = 0; i < q; i++)
	{
		getchar();
		char c = getchar();
		int x;
		scanf("%d",&x);
		if(c == 'A')
		{
			printf("%c\n",s[(start + x - 1 )% s.length()]);
		}
		if(c == 'M')
		{
			if(x > 0)
			{
				start += x;
			}
			else
			{
				start += x;
				if(start < 0)
				{
					start += s.length();
				}
			}
			//cout << start << endl; 
		}
	}
	return 0;
}

A:一共有n个鱼塘,每个鱼塘可能存在4种状态。针对每种状态有你可以进行的操作,问最多可以得到多少条鱼。

鱼塘状态:
1.没有鱼,没有鱼饵
2.有鱼,没有鱼饵
3.没有鱼,有鱼饵
4.有鱼,有鱼饵。

你可以进行的操作:(对于每个鱼塘,你只可以进行一次操作)
1.如果有鱼饵,你的鱼饵+1
2.如果有鱼,你的鱼+1
3.如果没有鱼,你可以花费1个鱼饵,得到1条鱼
4.什么都不做

很显然:鱼饵的目的也是为了得到鱼,所以鱼的优先级大于鱼饵。
即:如果鱼塘有鱼,有鱼饵,选择拿鱼。
其次,鱼饵只可以在没有鱼的鱼塘使用。

注意,遍历的顺序为从后往前,因为正常的顺序从前往后,你在贪心选择的时候,可能会出现:
你前面的鱼饵比较多,你贪心一时鱼饵换了鱼,有鱼饵没拿,但是后面有好多空鱼塘被浪费。
所以,倒序可以避免空鱼塘被浪费,使得鱼饵物尽其用。
倒序累计空鱼塘数目,碰到鱼饵就使用一个空鱼塘即可。
简而言之:保证空鱼塘等待鱼饵,而不是鱼饵等待空鱼塘

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
	{
		int n;
		cin >> n;
		string s;
		cin >> s;
		int ans = 0;//鱼 
		int sum = 0;//空鱼塘 
		for(int i = n-1; i >= 0; i--)
		{
			if(s[i] == '2' || s[i] == '3')
			{//优先拿鱼 
				ans++;
			}
			if(s[i] == '0')
			{
				sum++;
			}
			if(s[i] == '1')
			{
				if(sum > 0)
				{
					sum--;
					ans++;
				}
				else
				{
					sum++;
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

C:给你一个由20个点组成的手图形,20个点的顺序可能为顺时针,也可能为逆时针,手的大小固定,判断是左手还是右手。

很显然,大拇指长为6,手掌底为9,小指为8.
注意:小数!!找这三条边的时候,可能存在精度差异,由于图形的最短边为1,所以,我将精度定义为0.1,精度小于0.1都可以接受。

我自己ac的思路:
假设当前图为右手,找到四个点,这四个点ABCD,从大拇指上,经过手掌底,到达小指上。
枚举8种情况,手指方向指向 左上方,右上方,左下方,右下方 以及垂直x轴向上向下,垂直y轴向左向右。如果符合右手的特点,即为右手,否则为左手。
具体看代码,手指的指向由手指的斜率确定,由截距判断手的特点。

比较麻烦,代码长,复杂,但是便于理解。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 50;
const double eps = 1e-1;
struct node
{
    double x,y;
}a[maxn];
double dis(node a, node b)
{
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y)*(a.y-b.y);
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        for(int i = 0; i < 20; i++)
        {
            cin >> a[i].x >> a[i].y;
        }
        vector<int> v;
        for(int i = 0; i < 20; i++)
        {
            if(fabs(dis(a[i],a[(i+1)%20])-36) < eps && fabs(dis(a[(i+1)%20],a[(i+2)%20])-81) < eps)
            {
                v.push_back(i);
                v.push_back((i+1)% 20);
                v.push_back((i+2)% 20);
                v.push_back((i+3)% 20);
                //cout << "有" << endl;
                break;
            }
            if(fabs(dis(a[i],a[(i+1)%20])-81) < eps && fabs(dis(a[(i+1)%20],a[(i+2)%20])-36) < eps)
            {
                v.push_back((i+2)% 20);
                v.push_back((i+1)% 20);
                v.push_back(i);
                v.push_back((i - 1 + 20)% 20);
                //cout << "有" << endl;
                break;
            }
        }
         
        //cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << endl;
        node A = a[v[0]];
        node B = a[v[1]];
        node C = a[v[2]];
        node D = a[v[3]];
         
        double k;
        int ans = 0;//是右手
        if(A.y == B.y)
        {//斜率为0,垂直y轴
            if(A.x > B.x)
            {//向右
                if(A.y < C.y) ans = 1;
            }
            else
            {//向左
                if(A.y > C.y) ans = 1;
            }
        }
        else if(A.x == B.x)
        {//垂直x轴
            if(A.y > B.y)
            {//向上
                if(A.x > C.x)
                {
                    ans = 1;
                }
            }
            else
            {
                if(A.x < C.x) ans = 1;
            }
        }
        else
        {
            k = (A.y - B.y) / (A.x - B.x);
            double bab = A.y - k * A.x;
            double bdc = D.y - k * D.x;
            if(k > 0)
            {//右上下
                if(A.y < B.y)
                {//向右下
                    if(bab > bdc) ans = 1;
                }
                else
                {//向右上
                     
                    if(bab < bdc) ans = 1;
                }
            }
            else if(k < 0)
            {//左上下
                if(A.y > B.y)
                {//左上
                    if(bab > bdc) ans = 1;
                }
                else
                {//左下
                    if(bab < bdc) ans = 1;
                }
            }
        }
         
        if(ans == 0)
        {
            cout << "right" << endl;
        }
        else
        {
            cout << "left" << endl;
        }
    }
    return 0;
}

题解的思路:
先找到手掌底长度为9的边,按给出点的顺序,端点为A,B,C;
那么BC的长度肯定为6或者8.
让向量AB和BC进行叉乘,会发现:
叉乘结果为正数:逆时针
结果负数:顺时针
确定顺序之后,看bc的长度为6还是8,便可以判断左右手了

#include <iostream>
#include <algorithm>
using namespace std;
const double eps = 0.1;
struct node
{
	double x,y;
}a[25];
double dis(node a, node b)
{
	return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}
bool check(int A, int B, int C)
{
	double ans;
	ans = (a[B].x - a[A].x)*(a[C].y - a[B].y) - (a[B].y - a[A].y)*(a[C].x - a[B].x);
	if(ans > 0)
	{
		return 1;
	}
	else return 0;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
	{
		for(int i = 0; i < 20; i++)
		{
			cin >> a[i].x >> a[i].y;
		}
		int A,B,C;
		for(int i = 0; i < 20; i++)
		{
			if(fabs(dis(a[i],a[(i+1)%20])-81) < eps )
			{//先找到长度为9的边 
				A = i;
				B = (i+1)%20;
				C = (i+2)%20;
				break;
			}
		}
		if(check(A,B,C))
		{//逆时针 
			double l = dis(a[B],a[C]);
			if(fabs(l - 36) < eps)
			{
				cout << "left" << endl;
			}
			else if(fabs(l - 64) < eps)
			{
				cout << "right" << endl;
			}
		}
		else
		{//顺时针 
			double l = dis(a[B],a[C]);
			if(fabs(l - 36) < eps)
			{
				cout << "right" << endl;
			}
			else if(fabs(l - 64) < eps)
			{
				cout << "left" << endl;
			}
		}
	}
	return 0;
}

G:给你一个图,内有n个点和m条边,初始时第i个点上是第i种颜色。进行q次操作,问最终每个点的颜色。

很显然的并查集,但是一开始写超时了。
看了看其他人的思路,学到了启发式合并用于优化。(比较疑惑的是,我一开始是内存超限,而启发式合并是优化时间的…)

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 8e5 + 50;
int fa[maxn];
vector<int> map[maxn];
int find(int x)
{
	if(fa[x] == x) return x;
	else return fa[x] = find(fa[x]);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
	{
		int n,m;
		cin >> n >> m;
		for(int i = 0; i <= n; i++)
		{
			fa[i] = i;
			map[i].clear();
		}
		for(int i = 1; i <= m; i++)
		{
			int u,v;
			cin >> u >> v;
			map[u].push_back(v);
			map[v].push_back(u);
		}
		int q;
		cin >> q;
		
		while(q--)
		{
			int x;
			cin >> x;
			if(fa[x] != x) continue;
			vector<int> t = map[x];
			map[x].clear();
			for(int i = 0; i < t.size(); i++)
			{
				int v = find(t[i]);
				//当前x所连临界点的颜色 
				if(v != x)
				{
					fa[v] = x;
					//改颜色 
					//重点来了,启发式合并 
					//我们将小的集合 合并 到大的
					//与路径压缩一起,是并查集优化的两大方法 
					if(map[x].size() < map[v].size())
					{
						swap(map[x],map[v]);
					}
					//
					for(int j = 0; j < map[v].size(); j++)
					{
						map[x].push_back(map[v][j]);
					}
					map[v].clear(); 
				}
			}
		}
		for(int i = 0; i < n; i++)
		{
			cout << find(i) << " ";
		}
		cout << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值