牛客寒假算法基础算法训练营2

1、处女座与重修费

题目描述
期末考试结束了,处女座发现很多人挂了大物,只能等着第二年重修,还要交400元的重修费。处女座突然想起有个学长和他讲过,如果学校哪一年缺钱了,那一年的大物试卷就会特别难。现在处女座有了所有人的成绩,处女座想知道如果所有挂科的人都在第二年重修,学校能赚多少重修费?

挂科是指一门课的分数小于60分。

输入描述:
第一行一个整数n,表示考试的人数。
第二行n个整数,表示每个人的成绩。
1<=n<=10000
学生的成绩为0-100(包括0和100)之间的整数

输出描述:
一行,学校能赚的重修费用

示例1

输入
4
60
56
100
59

输出
800

这才是真正的签到题,没什么难度,给你点小自信假装很简单

#include
using namespace std;
int main()
{
	int n,cnt=0;
	cin>>n;
	while(n--)
	{
		int temp;
		cin>>temp;
		if(temp<60)cnt++;
	}
	cout<

2、处女座的期末复习

题目描述
快要期末考试了,处女座现在有n门课程需要考试,每一门课程需要花ai小时进行复习,考试的起始时间为bi,处女座为了考试可以不吃饭不睡觉,处女座想知道他能否复习完所有的科目(即在每一门考试之前复习完该科目)。每一门课的考试时间都为两小时。

输入描述:
第一行一个整数n
第二行n个整数a1,a2,…,an,表示每门课需要复习的时间
第三行n个整数b1,b2,…,bn,表示每门课考试的时间
1<=n<=105
0<=ai<=109
0<=bi<=109

输出描述:
如果处女座能复习完,输出”YES”,否则输出”NO”

示例1

输入
3
0 1 1
2 6 4

输出
YES

说明
在0-1小时复习第2门课,
在1-2小时复习第3门课,
在2-4小时考第1门课,
在4-6小时考第3门课,
在6-8小时考第2门课
备注:
考试时不能复习,保证考试时间不会重叠。
复习可以拆开,只要复习时间够了即可。

一个比较基础的贪心算法题,先根据输入的数据sort一下,当考试时间相同时按照复习需要时间,不相同时按照考试时间排序,之后从最早考试的那一科开始,如果满足已经用的时间<=考试时间就说明可以满足条件,已经用的时间为考试时间加上复习时间。

AC代码如下

#include
using namespace std;
struct Node {
	int time;
	int rank;
};
struct Node N[100005];
long long int t;
bool cmp(struct Node a,struct Node b)
{
	if(a.rank!=b.rank)
		return a.rank>n;
	t=0;
	for(int i=0;i>N[i].time;
	for(int i=0;i>N[i].rank;
	sort(N,N+n,cmp);
	int flag=1;
	for(int i=0;i

另外还看了几道题,勉强知道考点,实在是打击人,主办方重新定义“基础”这个词语的意思,坐等题解吧。

3、处女座与宝藏
题目描述
处女座进行了一次探险,发现了一批宝藏。如果他获得这批宝藏,那么他一辈子都不需要工作了。但是处女座遇到了一个难题。
宝藏被装在n个宝箱里,宝箱编号为1,2,…,n,只有所有宝箱在某一时间被打开,处女座才能获得宝藏。有m个开关,每个开关控制k个宝箱,如果按下一个开关,那么这k个宝箱的开关状态都会发生改变(从开启变成关闭,从关闭变成开启),处女座想知道他能否获得这批宝藏。

输入描述:
第一行两个正整数n,m,
第二行n个整数,每个整数为0或1,表示初始时宝箱的状态,0表示开启,1表示关闭
接下来m行,每行开头一个整数k表示这个开关控制的宝箱的个数,接下来k个整数,表示控制宝箱的编号
1<=n,m<=200000
1<=k<=n
题目保证每个宝箱最多被两个开关控制。

输出描述:
一行,如果处女座能获得宝藏,输出”YES”,否则输出”NO”示例1

输入
4 4
1 0 1 1
2 3 4
2 1 3
1 2
2 1 2

输出
YES

一道2-SAT题目,头一次听说,研究了题解的代码研究了几个小时,勉勉强强看懂,用到了找有向图最大连通分量的方法,难度对菜鸡的我实在是太大了,具体都写在注释里了。

#include 
using namespace std;
#define ll long long
 
const int MAXN = 400010;
const int MAXM = 800010;
struct Edge
{
    int to,next;
}edge[MAXM];
int head[MAXN],tot;//tot用于记录当前存入的边数 ,head用于记录最后一个输入的以这个点为起点的边 

void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}

void addedge(int u,int v)
{
    edge[tot].to = v; 
	edge[tot].next = head[u]; 
	head[u] = tot++;
}

int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int scc;
bool Instack[MAXN];
int num[MAXN];
 
 /*
 DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)
LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号
当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量
 */ 
void Tarjan(int u)//用于找最大强连通分量 
{
    int v;
    
    Low[u] = DFN[u] = ++Index;
    
    Stack[top++] = u;//用数组实现了一个堆栈 
    Instack[u] = true;//记录是否进栈 
    
    for(int i = head[u];i != -1;i = edge[i].next)
    {
    	
        v = edge[i].to;//边的终点 
        
        if( !DFN[v] )//相当于进行了一次深搜 
        {
            Tarjan(v);
            if(Low[u] > Low[v])
				Low[u] = Low[v];
        }
        
        else if(Instack[v] && Low[u] > DFN[v])
        	Low[u] = DFN[v];
    }
    
    if(Low[u] == DFN[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;//全都出栈 
            Belong[v] = scc;//当前在堆栈中的节点全都在一个强连通分量中 
            num[scc]++;//记录当前强连通分量里面有多少个点 
        }
        while(v != u);
    }
}
 
bool solvable(int n)//n表示宝箱个数的两倍 
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    memset(num,0,sizeof(num));
    Index = scc = top = 0;
    for(int i = 0;i < n;i++)
    	if(!DFN[i])
    		Tarjan(i);//找出所有的强连通分量
    		
    for(int i = 0;i < n;i += 2)
        if(Belong[i] == Belong[i^1])
        	return false;
    return true;//只有在所有宝箱在一个连通分量中才可以满足条件 
}
 
int n,m;
int a[200005];
vector v[200005];
 
int main()
{
    scanf("%d%d",&n,&m);
    init();
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);//记录初始状态 
    for (int i=0;i

4、处女座与复读机
题目描述
一天,处女座在牛客算法群里发了一句“我好强啊”,引起无数的复读,可是处女座发现复读之后变成了“处女座好强啊”。处女座经过调查发现群里的复读机都是失真的复读机,会固定的产生两个错误。一个错误可以是下面的形式之一:
1.将任意一个小写字母替换成另外一个小写字母
2.在任意位置添加一个小写字母
3.删除任意一个字母
处女座现在在群里发了一句话,他收到了一个回应,他想知道这是不是一个复读机。

输入描述:
两行
第一行是处女座说的话s
第二行是收到的回应t

s和t只由小写字母构成且长度小于100
输出描述:
如果这可能是一个复读机输出”YES”,否则输出”NO”

示例1

输入
abc
abcde

输出
YES

说明
abc->abcd->abcde

示例2

输入
abcde
abcde

输出
YES

说明
abcde->abcdd->abcde

备注:
只要能经过两步变换就从s得到t就有可能是复读机。

答案给出的是模拟的方法,很容易想明白,但是题目存在很多值得优化的地方,网上有大神用动态规划做的也AC,此外针对答案给出的代码,在循环结束的位置上还有可以优化的地方,下面代码是做了部分改动的AC代码,循环结束的条件加上&&can可以减少循环次数,此外还在变化一位的情况做了修改,应该是cnt==1时才符合条件,而不是cnt<=1,虽然效果是一样的。具体思路写在注释里了。

#include 
using namespace std;
string a,b;
 
int main()
{
    cin>>a>>b;
    if (fabs((int)a.length()-(int)b.length())>2)//缩小范围,如果复读机说的比原话长度还要差别2位以上说明肯定不是 
        puts("NO");
    else
    {
        if (b.length()==a.length()+2)//增加两位 
        {
            int can=0;
            for (int i=0;i

你可能感兴趣的:(牛客网寒假算法训练营)