题目: 1. 日期统计
问题描述
小蓝现在有一个长度为 100的数组,数组中的每个元素的值都在 0 到 9 的范围之内。数组中的元素从左至右如下所示:
5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2
7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3
现在他想要从这个数组中寻找一些满足以下条件的子序列:
- 子序列的长度为 8;
- 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且要求这个日期是 2023年中的某一天的日期,例如 20230902,20231223。yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。
请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 年的日期。对于相同的日期你只需要统计一次即可。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
题解:
#include <bits/stdc++.h>
using namespace std;
int main() {
int array[100] = {
5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,
5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,
2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3,
8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6,
1, 4, 0, 1, 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
};
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int ans = 0;
for (int month = 1; month <= 12; ++month) {
for (int day = 1; day <= daysInMonth[month]; ++day) {
int dateSeq[8] = {2, 0, 2, 3, month / 10, month % 10, day / 10, day % 10};
int k = 0;
for (int i = 0; i < 100; ++i) {
if (array[i] == dateSeq[k]) {
++k;
if (k == 8) {
ans++;
break;
}
}
}
}
}
printf("%d\n", ans);
return 0;
}
题目:2. 01串的熵
题解:
//首先要理解题目的意思
//不要被题目的数据吓到
//例如当S等于100时,
100的信息熵 =
-0的个数*(0的个数/总位数)*log2(0的个数/总位数)-1的个数*(1的个数/总位数)*log2(1的个数/总位数)
//然后我们 长度为23333333的01串 从0的个数为0开始枚举,直到1.0*23333333/2
(因为0的个数比1的个数少,所以一定不会超过23333333的一半)
//注意点:在判断浮点数是否等于一个数的时候不能if(x == y)
而是要判断它是否属于某一范围,或者二者差的绝对值属于某一范围一般取<0.01
#include<stdio.h>
#include<math.h>
int main(){
double n = 23333333,sum = 0;
int o = 0,l = 0;
for(o = 0;o <= n/2;o++){
sum = 0;
sum -= o*(o / n) * log2(o / n) + (n - o)*((n - o) / n) * log2((n - o) / n);
if(sum > 11625907.5 && sum < 11625907.6){
printf("%d",o);
break;
}
}
return 0;
}
题目:3. 冶炼金属
题解:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,a,b;
int max=INT_MAX,min=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d %d",&a,&b);
if(max>a/b){
max=a/b;
}
if(min<a/(b+1)+1){
min=a/(b+1)+1;
}
}
printf("%d %d",min,max);
return 0;
}
题目:4. 飞机降落
题解:
#include <iostream>
#include <vector>
using namespace std;
// 创建飞机结构体变量
struct plane
{
int t, d, l;
};
bool vis[15]; // true表示飞机降落,false表示飞机未降落
bool flag; // 标记是否全部安全降落
vector<plane> p(15);
// 深搜
void dfs(int m, int cnt, int last) // last表示此前所有飞机降落所需的单位时间
{
if (cnt == m)
{
flag = true;
return;
}
for (int i = 0; i < m; i++)
{
if (!vis[i] && p[i].t + p[i].d >= last) // 只有来的时刻+盘旋时间 > last 的飞机才可以安全降落
{
vis[i] = true;
dfs(m, cnt + 1, max(last, p[i].t) + p[i].l);
vis[i] = false;
}
}
}
int main()
{
int T;
cin >> T;
while (T--)
{
int N;
cin >> N;
for (int i = 0; i < N; ++i)
cin >> p[i].t >> p[i].d >> p[i].l;
flag = false;
dfs(N, 0, 0);
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
题目:5. 接龙数列
题解:
#include <iostream>
#include <string>
using namespace std;
int dp[10];
int main()
{
int n;
cin>>n;
string s;
int m=0;
for(int i=0;i<n;++i){
cin>>s;
int x=s[0]-'0',y=s[s.size()-1]-'0';
dp[y]=max(dp[x]+1,dp[y]);
m=max(m,dp[y]);
}
cout<<n-m<<endl;
return 0;
}
题目:6. 岛屿个数
题解:(没写出来,题解是别人的)
解题思路
首先明白用dfs搜索出一块岛屿,那么要解决岛屿是否是子岛的问题。由题意可知,子岛是在内海中的,题目需要查找的岛屿是在外海中的,并且外海是相连通的,可以用dfs搜索全部外海。从外海开始遍历岛屿,这样就遍历不到子岛
#include <stdio.h>
#include <stdlib.h>
int M, N, d[52][52];
void dfs_sea(int i, int j)
{
if ((i >= 0 && i <= M + 1) && (j >= 0 && j <= N + 1))
{
if (d[i][j] == 0)
{
d[i][j] = 2;//标记出外海
//八个方向
dfs_sea(i, j + 1);
dfs_sea(i, j - 1);
dfs_sea(i + 1, j);
dfs_sea(i + 1, j + 1);
dfs_sea(i + 1, j - 1);
dfs_sea(i - 1, j);
dfs_sea(i - 1, j + 1);
dfs_sea(i - 1, j - 1);
}
}
}
void dfs_island(int i, int j)
{
if ((i >= 0 && i <= M + 1) && (j >= 0 && j <= N + 1))
{
if (d[i][j] == 1)
{
d[i][j] = 3;//搜索过的岛屿不再搜索
dfs_island(i + 1, j);//右
dfs_island(i - 1, j);//左
dfs_island(i, j + 1);//上
dfs_island(i, j - 1);//下
}
}
}
int main(int argc, char *argv[])
{
// 请在此输入您的代码
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &M, &N);
//填充海水
for (int i = 0; i < N + 2; i++)
{
d[0][i] = 0;
d[M + 1][i] = 0;
}
for (int i = 1; i < M + 1; i++)
{
d[i][0] = 0;
d[i][N + 1] = 0;
}
//输入图
for (int i = 1; i < M + 1; i++)
{
for (int j = 1; j < N + 1; j++)
{
scanf("%1d", &d[i][j]);
}
}
dfs_sea(0, 0); //找出所有外海
int count;//计算岛屿数量
count = 0;
for (int i = 0; i < M + 2; i++)
{
for (int j = 0; j < N + 2; j++)
{
if (d[i][j] == 1 && d[i - 1][j] == 2)//只能从外海搜索岛屿,所以岛屿前一定是外海“2”
{
dfs_island(i, j);//搜索岛屿
count++;
}
}
}
printf("%d\n", count);
//以下代码可以打印出处理后的图
/*for (int i = 0; i < M + 2; i++)
{
for (int j = 0; j < N + 2; j++)
{
printf("%1d", d[i][j]);
if (j == N + 1)
printf("\n");
}
}*/
}
return 0;
}
题目:7. 子串简写
题解:
(70%,剩下百分之30运行超时了,暴力)
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
int k,ans=0;
char s[500050];
char c1,c2;
scanf("%d",&k);
scanf("%s %c %c",&s,&c1,&c2);
for(int i=0;i<strlen(s);i++){
if(s[i]==c1){
for(int j=i+k-1;j<strlen(s);j++){
if(s[j]==c2){
ans++;
}
}
}
}
printf("%d",ans);
return 0;
}
(全对的,前缀和)
#include<bits/stdc++.h>
using namespace std;
int n,l,c[500005];
long long sum;
char s[500005],a,b;
int main(){
scanf ("%d\n%s %c %c",&n,s+1,&a,&b);
l=strlen(s+1);
for(int i=1;i<=l;i++){
if (s[i]==a) c[i]++;
c[i]+=c[i-1];
if (i>=n&&s[i]==b){
sum+=c[i-n+1];
}
}
cout<<sum;
return 0;
}
题目:8. 整数删除
题解:
(只对百分之30,纯暴力,其他会超时)
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,k;
scanf("%d %d",&n,&k);
int a[n];
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int k1=k,n1=n;
while(k1--){
int min=0;
for(int i=0;i<n1;i++){
if(a[min]>a[i]){
min=i;
}
}
if(min==0){
a[min+1]=a[min+1]+a[min];
for(int i=min;i<n;i++){
a[i]=a[i+1];
}
}else{
a[min-1]=a[min-1]+a[min];
a[min+1]=a[min+1]+a[min];
for(int i=min;i<n1-1;i++){
a[i]=a[i+1];
}
}
n1--;
}
for(int i=0;i<n-k;i++){
if(i!=n-k-1){
printf("%d ",a[i]);
}else{
printf("%d",a[i]);
}
}
return 0;
}
(全对的,优先队列和双向列表,题解是别人的)
#include <iostream>
using namespace std;
#include<vector>
#include<queue>
#define int long long
const int N = 5e5 + 10;
int n, k;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que;//优先队列维护最小值
int pre[N], ne[N];//维护左边元素和右边元素的下标的下标
int cnt[N], a[N], tmp;//cnt代表下标为i的元素需要修改的值
signed main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> tmp;
que.push(make_pair(tmp, i));
pre[i] = i - 1;//下标为i的元素的左边元素的下标为i-1
ne[i] = i + 1;//下标为i的元素的右边的元素的下标为i+1
}
while (que.size() > n - k) {//查找k次
int num = que.top().first;//获取最小值
int id = que.top().second;//获取最小值的下标
que.pop();
/*这里cnt非0,说明在前面的操作过程中,该元素已经进行修改了,但是队列中还没有更新
现在对队列的这个值进行修正,修正后重新查找最小值*/
if (cnt[id]) {
que.push({ num + cnt[id],id });
cnt[id] = 0;
}
else {
int left = pre[id];
int right = ne[id];
cnt[left] += num;//对左边的值进行修改
cnt[right] += num;//对右边的值进行修改
//将该元素在双向链表中删除
ne[left] = right;
pre[right] = left;
}
}
while (!que.empty()) {
int num = que.top().first;
int id = que.top().second;
que.pop();
a[id] = num + cnt[id];
}
for (int i = 1; i <= n; i++) {
if (a[i]) {
cout << a[i] << " ";
}
}
return 0;
}
题目:9. 景区导游
题解:(没写出来,这是别人的题解)
//本题主要考察LCA
//若要求u->v的路径长度,等于求u->root+v->root - root->2*LCA(u,v)
//即分别从u和v走到根结点,再减去2倍的根结点到(u,v)的最近公共祖先的路径长度
//同理,若原本的遍历顺序是a->b->c,总路径长度为sum,若要跳过b,则新的路径长度为
//sum-dist(a,b)-dist(b,c)+dist(a,c),可画图验证
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+100;
int n,k;
int deep[N];//深度
int dp[N][21];//dp[i][j]表示从i结点开始跳2^j步可到达的结点
vector<int>edge[N];//边
vector<int>weight[N];//权值
ll path[N];//原始的游览路线
ll dist[N];//dist[i]存储i到根结点的距离
void dfs(int u,int father)//LCA的前置算法(模板)
{
deep[u]=deep[father]+1;
dp[u][0]=father;
for(int i=1;i<=20;i++)
{
dp[u][i]=dp[dp[u][i-1]][i-1];
}
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i],w=weight[u][i];
if(v==father)continue;
dist[v]=dist[u]+w;
dfs(v,u);
}
}
int LCA(int x,int y)//求x和y的最近公共祖先(模板)
{
if(deep[x]<deep[y])swap(x,y);
for(int i=20;i>=0;i--)
{
if(deep[dp[x][i]]>=deep[y])
{
x=dp[x][i];
}
}
if(x==y)return x;
for(int i=20;i>=0;i--)
{
if(dp[x][i]!=dp[y][i])
{
x=dp[x][i];
y=dp[y][i];
}
}
return dp[x][0];
}
ll get_dist(int x,int y)//求x和y的距离
{
if(x==0||y==0)return 0;
return dist[x]+dist[y]-2*dist[LCA(x,y)];
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)//插入n-1条无向边
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edge[u].push_back(v);
edge[v].push_back(u);
weight[u].push_back(w);
weight[v].push_back(w);
}
dfs(1,0);//跑一遍dfs为LCA做准备
ll sum=0;//sum存储原始游览路线的总路径长度
for(int i=1;i<=k;i++)
{
scanf("%d",&path[i]);
sum+=get_dist(path[i],path[i-1]);//依次累加
}
for(int i=1;i<=k;i++)//除去第i个景点
{
ll dist1=get_dist(path[i],path[i-1]);
ll dist2=get_dist(path[i],path[i+1]);
ll dist3=get_dist(path[i-1],path[i+1]);
printf("%lld ",sum-dist1-dist2+dist3);//套公式计算即可
}
printf("\n");
return 0;
}
题目:10. 砍树
题解: (没写出来,题解是别人的)
解题思路
1、理解树上点差分 2、理解树上边差分(本题的) 3、题目说要砍掉边,换句话说,答案这条边是这些路径的公共边,走过了m次。(不会树上差分就dfs,对当前道路走过的次数进行回溯) 4、最后dfs1可以O(n)的求出每条路径走过多少遍 cnt[i]表示编号为i的边走过多少次,l[i]表示标号为i的点对应的边
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e5+9;
ll n,m,dep[N],fa[N][24],cnt[N],l[N],ans=-1;
struct node
{
int point,s;
};
vector<node>g[N];
void dfs(int x,int p)
{
dep[x]=dep[p]+1;
fa[x][0]=p;
for(int i=1;i<=20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(const auto &y:g[x])
{
if(y.point==p)continue;
l[y.point]=y.s;
dfs(y.point,x);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void dfs1(int x)
{
for(const auto &i:g[x])
{
if(i.point==fa[x][0])continue;
dfs1(i.point);
cnt[l[x]]+=cnt[l[i.point]];
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
g[u].push_back({v,i}),g[v].push_back({u,i});
}
dfs(1,0);
for(int i=1;i<=m;i++)
{
int u,v;cin>>u>>v;
int p=lca(u,v);
cnt[l[u]]++,cnt[l[v]]++,cnt[l[p]]-=2;
}
dfs1(1);
//for(int i=1;i<=n;i++)cout<<cnt[i]<<' ';cout<<'\n';
for(int i=1;i<=n;i++)
if(cnt[i]==m)ans=i;
cout<<ans<<'\n';
return 0;
}
/*
7 3
1 2
1 3
2 4
2 5
3 6
3 7
4 7
3 5
6 1
*/