牛客月赛 45 A-E 题解

前言

链接
牛客小白月赛 45

第一次做完五题hh

写个题解试试看

代码都是直接copy的赛时代码,可能比较乱

我会尽量写注释hh
有问题也烦请大佬们指出hh

A

这题,难在读题。。。

如果 n >= x
那么可以反复横跳 ans = n*x

如果 n < x 那么他跳完第一次,就会掉下去摔死!
没有别的选择,因为这是悬崖不能不跳,而跳又跳不过去

因此,读入进行判读即可 复杂度o(1)

#include
#define endl "\n"
#define B 0x3f3f3f3f
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long int ll;
typedef pair PII;

int main()
{
	ll a,b;
	cin>>a>>b;
	if (a

B

找规律题 稍微模拟一下
发现 如果输入是 5

那么 第一层 dfs(1) ans+=1

然后进入第二层 dfs(1+2) ans+=3

… dfs(3+2) ans += 5

dfs(5+2) ans+=7
dfs(7+2) ans+= 9

发现,每一次加的数字为 1 3 5 7 9 11 …
直接等差数列求和公式

ans = (1+2*n-1) * n / 2 = n * n

规律就是直接 平方 ,复杂度 o(1)

#include
#define endl "\n"
#define B 0x3f3f3f3f
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long int ll;
typedef pair PII;

int main()
{
	ll a,b;
	cin>>a;
	b=(a)*a;
	cout<

C

是贪心?还是递推?

我们为了让分数最多,肯定要让小的糖果,尽可能地变成大糖果

那样,大糖果可以继续合成,使得分数继续增加

注意3 个 或者 4 个都可以合成

那么,尽可能利用3个的合成,比如说
有 12 个1级的糖果, 12 = 3 * 4 = 4 * 3

如果使用3的合成,那么会得到4个2级糖果,

而如果使用4的合成, 只能得到3个2级糖果。这是不利于最大化分数的

两种方式都是得到12分,但是多出的1个二级

可能会使得,2级可以继续向3级合成,使得分数变大

先给出代码再配合注释讲解

#include
#define endl "\n"
#define B 0x3f3f3f3f
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long int ll;
typedef pair PII;

ll ar[10];
ll ans;
int main()
{
	For(i,1,8)cin>>ar[i];
	For(i,1,8)
	{
		ll a = ar[i]/3;
        // a 是当前等级糖果按照3合成的数量
        // b 是余数
		ll b=ar[i]%3;
		if (a)
		{
        //a合成了,那么高级的糖果数量就会增加
			ar[i+1]+=a;
            //如果 ar[i] = 5 的话
            // a=1,b=2  , 此时按照3的合成方式,会剩下2个糖果
            //我们只能补上1个,使得按照4的方式进行,
           //不能把余数的2个都用上 因此我写了个min ,
           //其实赛后和朋友讨论,只需要特判 5 就够了hh
			b=min(b,a);
			ans+=(a*3+b)*i;
            // 计算分数 记得开LL
		}
	}
	cout<

只需要遍历8个数字,因此复杂度 o (1)

D

应该也算找规律题?
我是找规律贪心+快速幂算的。。还是说正解是DP吗?

思路

结论 2^(cnt) 就是答案

cnt 是配对成功的括号数量

这个结论是我赛时找规律发现的
当时并没有证明

先进行找规律

() 1个,不用切割
()()2个,要么中间切割,要么不切割

下面,将 使用 1 表示 () ,()() 直接写11

()()() 有4个

不切割 111

只切割1次

【1】 1,11 (), ()()

【2】 11,1 ()(), ()

切割2次 1,1,1 (), () , ()

一共4种

()()()() 这个有八种方法

不切割 1111

1
[1] 1,111

[2] 11,11

[3] 111,1

2
[1] 1,1,11

[2] 1,11,1

[3] 11,1,1

3

1,1,1,1

所以有8种

到这里,我们可以发现规律

() 1 对配对成功的括号,就2^0 = 1个

()() 2对,2^1=2

()()() , 2^3 = 8

()()()(), 2^4 = 16

所以说,答案就是2^ cnt 次方

当然,这个数字会特别大,需要 快速幂,模 1e9+7

#include
#define endl "\n"
#define B 0x3f3f3f3f
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long int ll;
typedef pair PII;

string s;
const int N=1e6+10;
int a[N],b[N],sum[N];
//快速幂
// 
ll qmi(ll a,ll b, ll p)
{
	ll ans=1;
    
	while(b)
	{
    
		if (b&1)ans=(ans*a)%p;
		b>>=1;
		a=(a*a)%p;
	}
	return ans;
}
int main()
{
	cin>>s;
	int cnt=0;
	int n=s.size();
	int f=1;
	
	For(i,1,n)
	{
		if (s[i-1]=='(')
		{
			a[i]=a[i-1]+1;
			b[i]=b[i-1];
		}
		else
		{
			b[i]=b[i-1]+1;a[i]=a[i-1];
		}
		if (f && b[i]>a[i])
		{
			f=0;
		}
		else if (b[i]==a[i])
		{
			sum[i]=++cnt;
		}
	}
    //这里先计算出, 成功配对的数量
    //然后要有一些判断条件
    //比如说,)( 这样右括号在左括号前面,就不可能成功
    // 或者,((() 这样数量对不上的,也不能成功
    //这些都输出 -1 即可
    // 注意是-1 , 不是0...
	ll ans=0,p=1e9+7;
	if (a[n]!=b[n] || !f || cnt==0)
	{
		ans=-1;
	}
	else
	{
    //这里就是快速幂辣hh
		ans=qmi(2,cnt-1,p);
		//cout<

我们需要遍历字符串来找到匹配成功的数量,因此复杂度o(N)

这里小小证明一下结论

在字符串合法的情况下, 每增加一对匹配成功的括号

可以理解为,把这对放在最后面,

xxxx AB x是之前的序列,AB是新的一对括号

要么在 xA之间切一刀, 要么不切

而且,这一刀切不切,和前面xxxx序列的方案数量是无关的

无后效性,就是贪心

因此 答案为 前面的 xxxx 的方案 * 2

也就是说,每多一对, 方案数*2

一直*2 *2 *2 ,所以就是指数级别的

E

坚持一下,最后一题辣hh

思路 树形DP 或者说DFS ?

从一个点开始,向他的每一条边进行遍历

会不断尝试是否要加上这一条边和这个点

如果,一条边 + 一个点的舒适度是个正数

那么,他就可以使得总体的舒适度更大

也就是说, 我们需要加上他

反过来说

如果一个点+一条边的舒适度是个负数

那加上他,只会使得总的舒适度变小

就选择不加

最后输出即可

上代码

#include
#define endl "\n"
#define B 0x3f3f3f3f
#define For(i,a,b) for (int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long int ll;
typedef pair PII;

const int N=1e6+10;
ll ar[N],st[N],h[N],e[N],ne[N],w[N];
ll n,m,idx,a,b,sum,c;
ll dp[N],ans;
//加边操作
void add(ll a,ll b,ll c)
{
	w[idx]=c;
	e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}

//核心函数
void dfs(ll x)
{
//为了避免重复访问一个点,利用st[] 进行标记
	if (st[x])return ;
	st[x]=1;
	dp[x]=ar[x];
//一个点自身的舒适度    
	
    
	for (int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];//遍历每一条出边
		if (st[j])continue;
		dfs(j);
        //经过这个dfs 函数后
        //dp[j] 的值已经被计算好了
        //进行判断
        
        
        //如果是个正数,可以让蚂蚁更舒服,把他加入
		if (dp[j]+w[i]>0)
		{
			dp[x]+=dp[j]+w[i];
		}
	}
	

}
int main()
{
	
	memset(h,-1,sizeof h);
	scanf("%lld",&n);
	ans=-INT_MAX;

//输入每一个点
//注意,需要保存最大的点的舒适度
//因为,如果所有的点,边舒适度都是负数
//那么,就不会加入他
//而我们不忍心让蚂蚁无家可归
//为了体现人道主义关怀
//一定会给一个,那给哪个呢?
//就是 相对而言最舒服的那个 矮子里面挑高个
	For(i,1,n)
	{
		scanf("%lld",ar+i);
		ans=max(ans,ar[i]);
	}
    
    //读入边,无向图,add 2次
	For(i,1,n-1)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		
		add(b,a,c);
		add(a,b,c);
	}

//进行遍历
	For(i,1,n)
	{
		if (!st[i])
		{
			dfs(i);
		}
	}

//记录最大的结果
	For(i,1,n)
	{
		ans=max(ans,dp[i]);
	}
	cout<

我们要不重复也不依赖地遍历每一个点,每一条边, 因此 复杂度是o(N)

学算法就上acwing!

完结撒花!

你可能感兴趣的:(c语言,算法,贪心算法)