前言
链接
牛客小白月赛 45
第一次做完五题hh
写个题解试试看
代码都是直接copy的赛时代码,可能比较乱
我会尽量写注释hh
有问题也烦请大佬们指出hh
A
这题,难在读题。。。
如果 n >= x
那么可以反复横跳 ans = n*x
如果 n < x 那么他跳完第一次,就会掉下去摔死!
没有别的选择,因为这是悬崖不能不跳,而跳又跳不过去
因此,读入进行判读即可 复杂度o(1)
#include<bits/stdc++.h>
#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<int,int> PII;
int main()
{
ll a,b;
cin>>a>>b;
if (a<b)
{
b=1;
}
a*=b;
cout<<a;
return 0;
}
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<bits/stdc++.h>
#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<int,int> PII;
int main()
{
ll a,b;
cin>>a;
b=(a)*a;
cout<<b;
return 0;
}
C
是贪心?还是递推?
我们为了让分数最多,肯定要让小的糖果,尽可能地变成大糖果
那样,大糖果可以继续合成,使得分数继续增加
注意3 个 或者 4 个都可以合成
那么,尽可能利用3个的合成,比如说
有 12 个1级的糖果, 12 = 3 * 4 = 4 * 3
如果使用3的合成,那么会得到4个2级糖果,
而如果使用4的合成, 只能得到3个2级糖果。这是不利于最大化分数的
两种方式都是得到12分,但是多出的1个二级
可能会使得,2级可以继续向3级合成,使得分数变大
先给出代码再配合注释讲解
#include<bits/stdc++.h>
#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<int,int> 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<<ans;
return 0;
}
只需要遍历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<bits/stdc++.h>
#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<int,int> 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<<cnt<<"\n";
}
cout<<ans;
return 0;
}
// ()()()()() 我比赛的时候其实还列了五个括号的hh
我们需要遍历字符串来找到匹配成功的数量,因此复杂度o(N)
这里小小证明一下结论
在字符串合法的情况下, 每增加一对匹配成功的括号
可以理解为,把这对放在最后面,
xxxx AB x是之前的序列,AB是新的一对括号
要么在 xA之间切一刀, 要么不切
而且,这一刀切不切,和前面xxxx序列的方案数量是无关的
无后效性,就是贪心
因此 答案为 前面的 xxxx 的方案 * 2
也就是说,每多一对, 方案数*2
一直2 2 *2 ,所以就是指数级别的
E
坚持一下,最后一题辣hh
思路 树形DP 或者说DFS ?
从一个点开始,向他的每一条边进行遍历
会不断尝试是否要加上这一条边和这个点
如果,一条边 + 一个点的舒适度是个正数
那么,他就可以使得总体的舒适度更大
也就是说, 我们需要加上他
反过来说
如果一个点+一条边的舒适度是个负数
那加上他,只会使得总的舒适度变小
就选择不加
最后输出即可
上代码
#include<bits/stdc++.h>
#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<int,int> 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<<ans;
return 0;
}
我们要不重复也不依赖地遍历每一个点,每一条边, 因此 复杂度是o(N)