蓝桥真题
有奖问答
这种选择导致分支可以使用递归
我个人觉得洛谷的答案错了,如果按能得到洛谷答案的代码,改成求30题对30道,最多对30道的话,得到的是0,应该把限制条件改为能计算答对10道题的方案,因为最多十道题不是不能达到10道题
DFS
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
int ans=0;
void work(int t,int m)//已答完的题和已经拿到的分数
{
if(m==7&&t==30) ans++;//求的是结束后的积分,所以==30,中途结束的可认为是自那之后一直没答对
if(t==31) return;
if(m==11) return;
work(t+1,m+1);
work(t+1,0);
}
int main() {
work(0,0);
cout<<ans;
return 0;
}
DP
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
int dp[40][40];
int main() {
dp[0][0]=1;
for(int i=1;i<=30;i++)
{
for(int j=0;j<=10;j++)
{
if(j<=9) dp[i][j+1]+=dp[i-1][j];
dp[i][0]+=dp[i-1][j];
}
}
cout<<dp[30][7];
return 0;
}
-------------------------------------------------------------------------------------------------- |
平方差
推柿子,得到 x=(y−z)×(y+z),可知 x 满足可以拆成 2 个奇偶性相同的数的乘积。
如果是奇数,直接拆成 1 和它本身即可。
如果是偶数,因为要拆成 2 个偶数,所以应是 4 的倍数,此时一种拆分为拆成 2 和 x 除以 2。
至此,答案为 l 到 r 中 奇数 和 4 的倍数 的个数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int f(int x) {//小于等于x的奇数个数
if (!x) return 0;
return (x + 1) / 2;
}
int g(int x) {//小于等于x的4的倍数个数
return x / 4;
}
int main() {
int l, r; cin >> l >> r;
cout << f(r) - f(l - 1) + g(r) - g(l - 1);
return 0;
}
-------------------------------------------------------------------------------------------------- |
更小的数
难得一遍过
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
string s;
LL ans;
int main() {
cin>>s;
for(int i=0;i<s.length()-1;i++)
{
for(int j=i+1;j<=s.length()-1;j++)
{
for(int k=1;k<=(j-i+1)/2;k++)
{
if(s[i+k-1]-'0'>s[j-k+1]-'0')
{
ans++;
break;//i-j区间已经查完,更小的子区间以后会查到,要break防止重复
/*cout<<s[i+k-1]<<" "<<s[j-k+1]<<" "<<i<<" "<<j<<endl;*/
}
else if(s[i+k-1]-'0'<s[j-k+1]-'0') break;
}
}
}
cout<<ans;
return 0;
}
-------------------------------------------------------------------------------------------------- |
做了一天,感觉自己真不是搞算法的料www
一开始把题目看错了,看成了不同的落子顺序也算不同的结局,一顿饭吃完还没跑完答案
其实是不算顺序,只看最终棋局
题目是DFS
每次下一个子,可以是 1 或者是 2 ,maps【i】【j】= 1 或者 2 .
先看下 1 的情况 ,maps【i】【j】= 1 , 然后另一个玩家落子 ,dfs ( i , j + 1 )
然后是 2 的情况 ,maps【i】【j】= 2 , 然后另一个玩家落子 ,dfs ( i , j + 1 ).
下到最后肯定会填满,满了之后检查 (check) 棋局是否满足要求,即没有 横 竖 斜 的 五子连珠
满足的话就 ans++;
之后return , 考虑 上一步棋的另一种情况
如果这步棋 1 , 2 都考虑了,那就这步棋撤回,maps【i】【j】= 0,
然后return ,考虑 上一步棋的另一种情况
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL ans,maps[6][6];
bool check()
{
int c1=0,c2=0;
for(int i=1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
if(maps[i][j]==1) c1++;
if(maps[i][j]==2) c2++;
}
}
if(c1-c2!=1) return true; //要符合条件的话,先手的13棋,后手12棋,先手-后手=1
for(int i=1;i<=5;i++)
{
bool t=true;//要注意检查每行是否有连珠,不能放外面初始化,不然一行没有连珠的话,下面可能连珠的当成没有连珠了,下面同理,因为这个卡了好久
for(int j=1;j<=5;j++)
{
if(maps[j][i]!=maps[1][i]) t=false;
}
if(t) return true;
}
for(int i=1;i<=5;i++)
{
bool t=true;
for(int j=1;j<=5;j++)
{
if(maps[i][j]!=maps[i][1]) t=false;
}
if(t) return true;
}
bool t=true;
for(int k=1;k<=5;k++)
{
if(maps[k][6-k]!=maps[1][5]) t=false;
}
if(t) return true;
t=true;
for(int k=1;k<=5;k++)
{
if(maps[k][k]!=maps[1][1]) t=false;
}
if(t) return true;
return false;
}
void dfs(int i,int j)
{
if(j>=6)
{
i++,j=1;
}
if(i>=6)
{
if(!check()) ans++;
return;
}
maps[i][j]=1;
dfs(i,j+1);
maps[i][j]=2;
dfs(i,j+1);
maps[i][j]=0;
return;
}
int main() {
dfs(1,1);
cout<<ans;
return 0;
}
-------------------------------------------------------------------------------------------------- |
训练士兵
挺简单的一道题,每次检查所有士兵的训练费用是否小于组团训练费用,小的话就全体单独训练,大于就组团训练
但是蒻蒟被困了一天,
因为我的方法是每次用 当前最小的次数 - 已经训练的次数 ,之前一直错误地把 已经训练的次数 当成 每次训练的次数的累加 了
(比较骄傲的就是DS都没发现,我发现了(不过这有什么好骄傲,不还是菜吗,这么低级的都会错))
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL n,S,ans,ttc,ttt;
struct P{
LL cost=0,time=0;
P()=default;
P(LL c,LL t):cost(c),time(t){};
bool operator<(const P & b)const{
return time>b.time;
}
};
priority_queue<P> p;
int main() {
cin>>n>>S;
for(int i=1;i<=n;i++)
{
LL a,b;
cin>>a>>b;
ttc+=a;
p.push(P(a,b));
}
while(!p.empty())
{
LL mc=0,mt=p.top().time;
if(ttc<=S)
{
ans+=ttc*(mt-ttt);
}
else
{
ans+=S*(mt-ttt);
}
while(!p.empty()&&p.top().time==mt)
{
mc+=p.top().cost;
p.pop();
}
ttc-=mc;
ttt=mt;//害人精,找了我一天
}
cout<<ans<<endl;
return 0;
}
-------------------------------------------------------------------------------------------------- |
数学检测题,一开始蒻蒟用的办法是:
先检测 k 个是否符合条件
记当前的序号为 i
然后从 k+1 开始 , 每次将前 i - 1 个数按减去平均数并 abs 后的大小(即波动程度)排序 , 将差值最大的 i - 1 与 i 比较 , 若是 i 的波动更小,则替换掉 i - 1 否则往后推
但是这样做会 TLE
所以应该用复杂度更小的二分查找
方差 = 每个数^2 / n - 平均数^2.
每次二分后要在区间内排序,之后遍历每个 k 长度的区间计算方差 , 取最小 ,小则 r = mid - 1 , 大于等于则 l = mid + 1
n * log n * k 还是不够,而遍历 k 长度区间是求 和 与 平方和 , 用前缀和
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL a[100010],tot[100010],pown[100010],b[100010],n,k,t;
bool check(int x)
{
for(int i=1;i<=x;i++) b[i]=a[i];
sort(b+1,b+1+x);
for(int i=1;i<=x;i++)
{
tot[i]=tot[i-1]+b[i];
pown[i]=pown[i-1]+b[i]*b[i];
}
double ans=1e300;
for(int i=k;i<=x;i++)
{
//从每个 i 开始检查,一开始写了 [ x ] 呜呜呜
ans=min(ans,((1.0*pown[i]-1.0*pown[i-k])/k)-( (1.0*tot[i]-1.0*tot[i-k]) / k )*( (1.0*tot[i]-1.0*tot[i-k]) / k ));
}
return ans<t;
}
int main() {
cin>>n>>k>>t;
for(int i=1;i<=n;i++) cin>>a[i];
int l=k,r=n,ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
cout<<ans;
return 0;
}
-------------------------------------------------------------------------------------------------- |
dp题
当前状态可以按如下方式摆放:
这个摆放可一直延伸到 F(0) (L型方块存在时,由于可以转动放置,方案数*2),故可得到如下推导:
然后手动dp即可
不LL,一场空
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e7+10;
LL n,mod=1000000007,ans,dp[N];
int main() {
cin>>n;
dp[1]=1,dp[2]=2,dp[3]=5;
for(int i=4;i<=n;i++)
{
dp[i]=(2*dp[i-1]%mod+dp[i-3]%mod)%mod;
}
cout<<dp[n]<<endl;
return 0;
}
-------------------------------------------------------------------------------------------------- |
数位反转
反转操作:
int re(int a)
{
int res=0;
while(a)
{ //想象一下栈的进出,类似这种感觉
res=(res<<1)+(a&1);
a>>=1;
}
return res;
}
如果 dp 的是数的值的话,就相当于累加,可能会爆
选择 dp 反转后与原来的差值 , 最后选择最大的和最初的累加 sum 相加
当前有 转 or 不转 ,当前最大值取决于 前面转不转
如果当前 转 (1) , 则前面 不转(0) ———— i-1 的反转次数 j-1 , 转(1)———— i-1 的反转次数 j 不变
如果当前 不转(0) , 则前面 不转(0) ———— i-1 的反转次数 j , 转(1)———— i-1 的反转次数 j 不变
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
int n,m;
LL dp[1010][1010][2],t,sum,cha,d[1010];
int re(int a)
{
int res=0;
while(a)
{
res=(res<<1)+(a&1);
a>>=1;
}
return res;
}
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>t;
sum+=t;
d[i]=re(t)-t;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+d[i];
}
}
for(int i=1;i<=m;i++) cha=max(cha,max(dp[n][i][0],dp[n][i][1]));
//因为不一定反转后会变大,所以要遍历,看哪个最大
cout<<sum+cha;
return 0;
}
-------------------------------------------------------------------------------------------------- |
正常思路:
每个瓜 买 | 买一半 | 买整个
买一半 ——> cnt+1
三种状态 dfs 一遍
sum == m ,ans = min ( ans , cnt )
return
如果 now == n+1 , return
优化:
当 cnt > ans ,return 剪枝
当 sum > m ,return 剪枝
但是还是 3^n 的复杂度
重点:
折半搜索
将 n 拆成两半 ,每次 dfs 其中一个
dfs 前一半时 , 到最后将 sum 与对应的 cnt 记录
dfs 后一半时 , 到最后将 sum 与前一半最后的 sum 相加 , 如果存在对应的 cnt ,则 当前cnt 与对应的 cnt 相加后,与 ans 比较 , 更新答案
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL n,m,ans=LLONG_MAX,a[40],N;
unordered_map <LL,LL> q;//记录折半后,一半点的 sum -- cnt 对应关系
//快读压下时间
LL read()
{
LL res=0;
char ch=getchar();
while(!isdigit(ch)&&ch!=EOF) ch=getchar();
while(isdigit(ch))
{
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
void dfs1(int now,LL sum,LL cnt)
{
if(cnt>ans||sum>m) return;
//当前 劈瓜次数 大于 记录最小值 ,剪枝
//当前 买瓜总重量 大于 需要值 ,剪枝
if(sum==m)
{
ans=min(ans,cnt);//刚好买够,更新答案
return;
}
if(now==N+1)
{
//前提是 买瓜总重量 小于等于 需要值 , 才能记录,不然后面也没得买(不过之前剪枝了为什么还要这样 ? 不懂啊抄题解的qwq)
if(sum<=m)
{
//将当前 买瓜重量 需要的 劈瓜次数 记录为最少那个
if(q.count(sum))
{
q[sum]=min(q[sum],cnt);
}
else q[sum]=cnt;
}
return;
}
dfs1(now+1,sum,cnt);//生瓜蛋子,不买
dfs1(now+1,sum+a[now],cnt+1);//劈瓜!
dfs1(now+1,sum+(a[now]<<1),cnt);//这瓜保熟,买整个
}
void dfs2(int now,LL sum,LL cnt)
{
if(cnt>ans||sum>m) return;
if(sum==m)
{
ans=min(ans,cnt);
return;
}
if(now==n+1)
{
//如果后半段的 sum 存在一个 前半段记录的 sum 与之相加 == m ,则可以刚好买够瓜
if(q.count(m-sum))
{
ans=min(ans,q[m-sum]+cnt);
}
return;
}
dfs2(now+1,sum,cnt);
dfs2(now+1,sum+a[now],cnt+1);
dfs2(now+1,sum+(a[now]<<1),cnt);
}
int main() {
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
//排序,小到大 或者 大到小,看评论好像 小到大 蓝桥官网过不了,大到小洛谷过不了,怎么绘世呢 qwq
sort(a+1,a+1+n);
m<<=1;
N=n>>1;
//搜前半段
dfs1(1,0,0);
//搜后半段
dfs2(N+1,0,0);
//printf 更快,再贪一点 qwq
printf("%lld",(ans==LLONG_MAX)? -1:ans);
return 0;
}
-------------------------------------------------------------------------------------------------- |
意思是当前节点的 兄弟节点 变为 右子节点(成一条),子节点 变成 左节点 (一个,这个节点重复以上操作)
由于 根节点 1 的兄弟节点数量是固定的 , 所以要找出 子节点中完成操作后长度最大 的子节点放在最后 , 决定深搜
不知道哪个子节点的子节点数量最大,要逐一dfs,找到最大得到,决定dp
每个节点的所有子节点数量为 该点的 size ,
最大的 子节点的子节点的数量为 该节点的 num
dp [ now ] == size + num ;
看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5+10;
LL n,head[N],cnt,dp[N],size[N];
LL read()
{
LL res=0;
char ch=getchar();
while(!isdigit(ch)&&ch!=EOF) ch=getchar();
while(isdigit(ch))
{
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
struct Edge{
int nxt,to,size;
}edge[N<<1];
void add(int from,int to)
{
edge[++cnt].to=to;
edge[cnt].nxt=head[from];
head[from]=cnt;
size[from]++;
}
void dfs(int now)
{
for(int i=head[now];i;i=edge[i].nxt)
{
int to=edge[i].to;
dfs(to);
dp[now]=max(dp[now],dp[to]);
}
dp[now]+=size[now];
}
int main() {
n=read();
for(int i=2;i<=n;i++)
{
int from=read();
add(from,i);
}
dfs(1);
cout<<dp[1];
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理