A. Everyone Loves to Sleep
题意:有n个闹钟在不同时间想起,如果在某个时间睡觉,在下一个闹钟响起前睡几小时几分。
思路:将所有闹钟时间转化成分钟,与当前时间计算,注意考虑当前时间可以和第二天的闹钟有联系,因此有可能会被第二天最早的闹钟吵醒。
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=1e5+10;
- int a[maxn];
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- int n;
- cin>>n;
- int h,m;
- cin>>h>>m;
- int st=h*60+m;//当前睡觉时间
- for(int i=1;i<=n;i++)
- {
- cin>>h>>m;
- a[i]=h*60+m;
- }
- sort(a+1,a+1+n);//排序
- int ans=-1;
- for(int i=1;i<=n;i++)
- if(a[i]>=st)
- {
- ans=a[i]-st;
- break;
- }
- if(ans==-1)//如果今天不再有闹钟吵醒
- ans=24*60-st+a[1];
- cout<
60<<' '<60<<'\n'; - }
- return 0;
- }
B. Remove Prefix
题意:有一个数组,可以进行操作:将首个元素删除。问至少进行多少次操作,才能让数组元素互不相同.
思路:记录数字的种类数量,当数组的长度等于种类数时,条件即成立,每增加一个数字时,如果原本这种数字的数量为0,那么种类数就会加1,减少一个数字时,如果减少这个数字后数字的数量为0,那么种类数就会减1。遍历一遍即可。
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e5+10;
- int a[maxn];
- int cnt[maxn];
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- int sum=0;//记录种类数
- int n;
- cin>>n;
- for(int i=1;i<=n;i++)
- cnt[i]=0;
- for(int i=1;i<=n;i++)
- {
- cin>>a[i];
- if(cnt[a[i]]++==0)
- sum++;
- }
- int i=1;
- if(sum==n)//如果当前种类数等于n
- {
- cout<<0<<'\n';
- continue;
- }
- int ret=n;//剩余数字
- for(i=1;i<=n;i++)
- {
- if(--cnt[a[i]]==0)
- sum--;
- ret--;
- if(ret==sum)
- break;
- }
- cout<'\n';
- }
- return 0;
- }
C. Minimum Varied Number
题意:给一个数,将其分解成若干个互不相同的0-9的数字,再将这些数字拼接成最小的数字。
思路:优先选取更大的数字,因为这样可以使拼接后的数字位数,且高位数字也会因为低位数字选取大了而变小。
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e5+10;
- int a[maxn];
- int cnt[maxn];
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- int s=0;
- int n;
- cin>>n;
- for(int i=9;i>=1&&n;i--)//0不需要考虑
- {
- if(n>=i)
- {
- a[++s]=i;
- n-=i;
- }
- }
- for(int i=s;i>=1;i--)
- cout<<'\n';
- }
- return 0;
- }
D. Color with Occurrences
题意:给一个字符串t和n个字符串s1...sn,每次可以进行染色操作,选取一个si,寻找t的与之相同的子串将其染成红色,问最少要多少次染完,并且依次输出每次用了哪个字符串和从哪开始染。
思路:设某字符串si可以给t染色,从l到r,那么假设l前面全都染完了。那么染完r的最少次数就是染完l-1到r-1其中一个最小操作数加1,利用动态规划,设dp[r]为染到r所花费的最小次数,那么根据上面所说转移方程就是dp[r]=min(dp[r],d[i]+1) (l-1<=i<=r-1),当一个dp值有所更新是,用nx数组保存该状态是从哪个状态转移过来的,(比如刚刚转移方程,nx[r]=i),还需要记录当前字符串si的编号以及其实位置l,最后,从t的末尾往前遍历输出即可。
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e4+10;
- const int inf=0x3f3f3f3f;
- int dp[maxn];
- int ansl[maxn];
- int ansid[maxn];
- int nx[maxn];
- char t[maxn],s[11][maxn];
- int main()
- {
- int w;
- cin>>w;
- while(w--)
- {
- int n;
- scanf("%s",t+1);
- cin>>n;
- for(int i=1;i<=n;i++)
- scanf("%s",s[i]+1);
- int m=strlen(t+1);//记录字符串的长度
- for(int i=1;i<=m;i++)//初始化
- dp[i]=inf;
- for(int i=1;i<=m;i++)//遍历t的1-m的位置
- {
- for(int j=1;j<=n;j++)//对于每个位置,使用s中的字符串尝试转移
- {
- int len=strlen(s[j]+1);
- int k=0;//记录t从i-len+1到i的子串和sj是否匹配
- while(i-k>=1&&len-k>=1&&t[i-k]==s[j][len-k])
- k++;
- if(k!=len)
- continue;
- for(int q=i-1;i-len<=q;q--)//从l-1到r-1进行转移,也就是i-len到i-1
- {
- if(dp[i]>dp[q]+1)//如果成功更新
- {
- dp[i]=dp[q]+1;
- nx[i]=q;//i状态从q状态转移来的
- ansid[i]=j;
- ansl[i]=i-len+1;
- }
- }
- }
- }
- if(dp[m]==inf)//如果未更新
- cout<<-1<<'\n';
- else
- {
- cout<
'\n'; - for(int i=m;i;i=nx[i])
- cout<
' '<'\n'; - }
- }
- return 0;
- }
E. Add Modulo 10
题意:给n个数字,对于每个数字可以进行任意次加上它的个位数的操作(x+x%10),问是否能让n个数字相等。
思路:当个位数为奇数时,进行操作必定变成偶数,所以只要研究个位数为偶数即可
0:0
2:2 4 8 16 22
4:4 8 16 22 24
6:6 12 14 18 26
8 16 22 24 28
先考虑0的情况,0无法改变数值了,因此当存在0时,要么此时数字全相等,要么就No,然后就是248,可以发现6所处的高位数字的奇偶性和2 8不同((x/10)%2),也就数说6和这两个数字的高位奇偶性不能相同。更近一步,把每个数字的个位变成2,判断高位奇偶性是否相同即可
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e5+10;
- ll a[maxn];
- int pos[20];
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- int n;
- cin>>n;
- int cnt=0;//记录是否有个位为0的情况
- for(int i=0;i<=10;i++)
- pos[i]=-1;
- for(int i=1;i<=n;i++)
- {
- cin>>a[i];
- while(a[i]%10!=2&&a[i]%10)//将所有数字个位变成0或2
- a[i]+=a[i]%10;
- if(a[i]%10==0)
- cnt++;
- }
- bool f=1;
- if(cnt!=0)//若有0,判断是否都相等
- {
- for(int i=1;i
- if(a[i]!=a[i+1])
- {
- f=0;
- break;
- }
- }
- else//个位都是2
- {
- for(int i=1;i
- {
- if((a[i]/10)%2!=(a[i+1]/10)%2)//判断所有高位奇偶性是否都相同
- {
- f=0;
- break;
- }
- }
- }
- if(f)
- cout<<"Yes\n";
- else
- cout<<"No\n";
- }
- return 0;
- }
F. Build a Tree and That Is It
题意:给n个点,以及1到2,2到3,3到1的距离(d12,d23,d31),问是否能建出一棵树,能建就输出一棵树。
思路:1 2 3能形成两种情况,第一种三个点在同一条链上,那么就是dxy+dyz=dxz,此时将中间的点作为根节点,向左右两边建链即可。当三个点不在同一条链上,那么就是dxy+dyz>dxz,肯定有一个交界点,将这个交界点作为根节点,伸出三条链,要求这个根节点到三点距离:
dis1=(d12+d13-d23)/2
dis2=(d12+d23-d13)/2
dis3=(d13+d23-d12)/2
其中dis不能有0.5的形式,因此括号内的值必须为偶数,此外,dis1+dis2+dis3+1为当前建出的树的节点数,不能超过n。
建完基本的树后,如果总节点数没到n,额外随便建点
不能存下dxy+dyzdxy+dyz+dxz
代码实现:
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e5+10;
- int d[4];//记录d12 d23 d31
- int dis[4];
- int i=4;//i为新建的点,从4开始
- void print(int root,int u)//建立根节点到该点的链
- {
- int d=dis[u];
- if(d==1)//如果中间没有新建立的点
- cout<
' '<'\n'; - else
- {
- d-=2;
- cout<
' '<'\n'; - i++;
- for(;d>0;d--)//中间新点形成链
- {
- cout<-1<<' '<'\n';
- i++;
- }
- cout<-1<<' '<'\n';//建立新点和目标点的链
- }
- }
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- i=4;
- int n;
- cin>>n;
- for(int i=1;i<=3;i++)
- dis[i]=0;
- for(int i=1;i<=3;i++)
- cin>>d[i];
- if(d[1]>=n||d[2]>=n||d[3]>=n)//当某两点距离大于n-1时
- {
- cout<<"NO\n";
- continue;
- }
- int sum=0;
- for(int i=1;i<=3;i++)//寻找三点距离之间的关系
- sum+=d[i];
- int root=0;
- int link=0;
- bool f=0;
- for(int i=1;i<=3;i++)
- {
- if(sum==d[i]*2)//一条链
- {
- link=i;//记录这条链的编号
- root=i-1;
- if(root==0)
- root=3;
- }
- if(sum
2)//其中一个距离比另外两个和大 - f=1;
- }
- if(f)
- {
- cout<<"NO\n";
- continue;
- }
- if(root!=0)//成了一条链
- {
- int da,db,a,b;//与另外两点的距离的编号和两点的编号
- //d12 d23 d31
- //若d12为链的总长,那么3为根节点,a为2,b为1,da=2,db=3
- //若d23为链的总长,那么1为根节点,a为3,b为2,da=3,db=1
- //若d31为链的总长,那么2为根节点,a为1,b为3,da=1,db=2
- da=(link+1)%3;
- db=(link+2)%3;
- a=(root+2)%3;
- b=(root+1)%3;
- if(da==0)
- da=3;
- if(db==0)
- db=3;
- if(a==0)
- a=3;
- if(b==0)
- b=3;
- dis[a]=d[da];//加入dis数组,方便在print取出
- dis[b]=d[db];
- cout<<"YES\n";
- print(root,a);
- print(root,b);
- }
- else
- {
- int d1=d[1]+d[3]-d[2];
- int d2=d[2]+d[1]-d[3];
- int d3=d[3]+d[2]-d[1];
- if(d1%2||d2%2||d3%2)//若不为偶数
- {
- cout<<"NO\n";
- continue;
- }
- d1/=2;
- d2/=2;
- d3/=2;
- if(d1+d2+d3+1>n)//所需点大于n
- {
- cout<<"NO\n";
- continue;
- }
- dis[1]=d1;
- dis[2]=d2;
- dis[3]=d3;//方便print取出
- root=i;//根节点为新建一个4
- i++;
- cout<<"YES\n";
- print(root,1);
- print(root,2);
- print(root,3);
- }
- if(i
1)//如果还缺,新增一条链 - {
- cout<
' '<'\n'; - i++;
- for(;i<=n;i++)
- {
- cout<-1<<' '<'\n';
- }
- }
- }
- return 0;
- }
G. Path Prefixes
题意:有一棵以1为根节点的树,一条边有两个边权a和b,定义r为从根节点到节点u的路径最长的前缀,这个前缀由1到u前面若干个边构成,但是其b值的和不能超过1到u整条路径a的和。问2-n每个点的r值。
思路:进行dfs,记录到达当前点a的和从0到dep深度的b的所有前缀和,pre[dep]=pre[dep-1]+b[to],因为前缀和是从小到大,并且已知a的和以及1到u选取边的情况的所有b值的和,就可以根据前缀和数组进行二分查找,然后每个点都二分查找记录答案即可
代码实现
- #include
- using namespace std;
- #define ll long long
- const int maxn=2e5+10;
- vector<int>vv[maxn];//存图
- ll a[maxn],b[maxn];
- ll pre[maxn];//记录前缀和
- int ans[maxn];//记录答案
- void add(int u,int v)
- {
- vv[u].push_back(v);
- }
- int search(ll num,int r)//二分查找
- {
- ll l=0;//保证l肯定能满足条件
- while(l
- {
- int mid=(l+r+1)>>1;
- if(pre[mid]<=num)
- l=mid;
- else
- r=mid-1;
- }
- return l;
- }
- void dfs(int u,ll now,ll nowpre,int dep)
- {
- pre[dep]=nowpre;//记录到达当前深度的前缀和
- ans[u]=search(now,dep);
- for(int to:vv[u])
- dfs(to,now+a[to],nowpre+b[to],dep+1);
- }
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- cin>>t;
- while(t--)
- {
- int n;
- cin>>n;
- for(int i=1;i<=n;i++)
- vv[i].clear();
- for(int i=2;i<=n;i++)
- {
- int fa;
- cin>>fa>>a[i]>>b[i];//因为是一棵树,所有点都只会有一条指向它的边,因此可以直接将ab当成点权
- add(fa,i);
- }
- dfs(1,0,0,0);//1节点深度为0,前缀和为0,a的和为0
- for(int i=2;i<=n;i++)
- if(i==n)
- cout<
'\n'; - else
- cout<
' '; - }
- return 0;
- }
-
相关阅读:
【Audio音频开发】音频基础知识及PCM技术详解
Python 完美诠释"高内聚"概念的 IO 流 API 体系结构
Windows11环境下安装Vmware Workstation 16的方法
二叉树的概念、存储及遍历
其他重要协议(DNS,ICMP,NAT,交换机)
redis 主从复制
27.【C/C++ 最全vector数组的用法 (详解)】
【进程间通信IPC】- 信号量的学习
C语言学习教程
留意差距:弥合网络安全基础设施的挑战
-
原文地址:https://blog.csdn.net/m0_63737271/article/details/126152258