• 码题集习题


     目录

    数对连图

    小码哥的花园

    最短字符串

    找四边形

    寻找正多边形

    数字串解密

    芯片刻蚀

    骑士锦标赛

    序列加法


            

            

    数对连图

    可以看出给出的图其实是不完整的无向图,而k的数对其实是一个完整的无向图。那么就要考虑n和k的大小关系了:

    • 当n小于等于k的时候(这个比较简单),完整的无向图必定包含不完整的,所以一定可以代替全部的边。
    • 当n比k大1时,因为在给每个点编号时一共1~n-1号点都可以分到唯一的编号,而n点就多出来了,那么这个点是不是就一定分配不到编号了呢? 额!其实不是的,这个多出来的点其实可以分配1~n-1编号中的任意一个编号,只需要保证没有用到重复的数对即可。

     所以这个题就转化成了:图中必有两个点的编号相同的情况,我们只需要每两个点取同一个编号的情况,然后看看该情况下有多少个重复的数对即可,然后有多少就扣掉多少个。就是一种答案

    红色的是原顶点号,绿色的是我们赋值的编号,可以看出有两个顶点的编号是一样(4和5的编号都是4),然后只需要<2,5>和<2,4>这个两个边扣除掉一个就行。

    1. #include
    2. using namespace std;
    3. const int N=107;
    4. int k,n,m,ma[N][N],ans;
    5. int main(){
    6. cin>>k>>n>>m;
    7. int u,v;
    8. for(int i=1;i<=m;i++){
    9. cin>>u>>v;ma[u][v]=ma[v][u]=1;
    10. }
    11. if(n<=k){
    12. cout<
    13. }
    14. else{
    15. for(int i=1;i<=n;i++)
    16. for(int j=i+1;j<=n;j++)//枚举所有的组合方案
    17. {
    18. int cnt=0;
    19. for(int l=1;l<=n;l++)
    20. if(ma[l][i]&&ma[l][j])cnt++;
    21. ans=max(ans,m-cnt);
    22. }
    23. cout<
    24. }
    25. return 0;
    26. }

            

            

    小码哥的花园

    就是在说在下一个点,使得图中存在的最大的正方形。

    注意不要陷入思维僵局,你判断是否存在正方形时,不要想着直接就去选四个点,然后判断一下位置关系,这样子没法做的。

    我们去考虑一下正方形的性质,什么情况下可以唯一确定一个正方形呢?并不是边,而是对角线。

    也就是说,我们只需要选两个点作为对角点,那么另外两个点既然已经唯一确定了,那么当然可以直接求出来的。求出来后去判断一个有几个点是春之花,如果是4个或3个,那么再看看能不能在这里种下一个春之花,最终取最优解 

    不妨设1,2,3,4的坐标为(x1,y1),(x2,y2),(x3,y3),(x4,y4) 

    易发现:x1+x2=x3+x4     y1+y2=y3+y4

    注意还有:y1-y2=x4-x3    x1-x2=y3-y4

    联立四个式子求解:

    2x4=x1+x2+y1-y2

    2x3=x1+x2-y1+y2

    2y3=y1+y2+x1-x2

    2y4=y1+y2-x1+x2

    1. #include
    2. using namespace std;
    3. int n,ans;
    4. char ma[107][107];
    5. int main(){
    6. cin>>n;
    7. for(int i=0;i>ma[i];
    8. int x1,x2,y1,y2,x3,x4,y3,y4;
    9. for(int x1=0;x1
    10. for(int y1=0;y1
    11. for(int x2=0;x2<=x1;x2++)//保证2号点在1号点的左上方,以便去重
    12. for(int y2=0;y2<=y1;y2++){
    13. x3=x1-y1+x2+y2;y3=x1+y1-x2+y2;x4=x1+y1+x2-y2;y4=-x1+y1+x2+y2;
    14. if(x3&1|x4&1|y3&1|y4&1) continue;
    15. x3/=2;y3/=2;x4/=2;y4/=2;
    16. if(x3<0||x3>=n||y3<0||y3>=n||x4<0||x4>=n||y4<0||y4>=n) continue;
    17. int cnt=0;
    18. if(ma[x1][y1]=='B'||ma[x2][y2]=='B'||ma[x3][y3]=='B'||ma[x4][y4]=='B')continue;
    19. if(ma[x1][y1]=='J')cnt++;
    20. if(ma[x2][y2]=='J')cnt++;
    21. if(ma[x3][y3]=='J')cnt++;
    22. if(ma[x4][y4]=='J')cnt++;
    23. if(cnt>=3)ans=max(ans,(x3-x1)*(x3-x1)+(y3-y1)*(y3-y1));
    24. }
    25. }
    26. cout<
    27. return 0;
    28. }

            

            

    最短字符串

    就是s1,s2,s3进行全排列,然后对每种情况枚举找最优解,我们需要预处理3个子串中两两子串的共同前缀个数和共同后缀个数,之后模拟时候直接使用就行。

    不过有个坑就是要注意某一个串是另一个串的子串的情况。这个时候可以直接忽略掉一方,不过你又要写两个两个排列的代码了,工作量又会增加。

    有一种更好的办法,就是把被包含的那个子串变成包含它的那个串,然后依然使用原来的f函数也能处理。

    1. #include
    2. using namespace std;
    3. int ans=(int)1e9;
    4. string s1,s2,s3;
    5. string merge(string a,string b){
    6. int l1=a.size(),l2=b.size();
    7. int minn=min(l1,l2);
    8. for(int i=minn;i>=1;i--){//注意要从长到短来
    9. string aa=a.substr(l1-i);
    10. string bb=b.substr(0,i);
    11. if(aa==bb)return a+b.substr(i);
    12. }
    13. return a+b;
    14. }
    15. int f(string a,string b,string c){
    16. int len=merge(merge(a,b),c).size();
    17. return len;
    18. }
    19. int main(){
    20. cin>>s1>>s2>>s3;
    21. if(s1.find(s2)!=-1)s2=s1;
    22. if(s2.find(s1)!=-1)s1=s2;
    23. if(s1.find(s3)!=-1)s3=s1;
    24. if(s3.find(s1)!=-1)s1=s3;
    25. if(s2.find(s3)!=-1)s3=s2;
    26. if(s3.find(s2)!=-1)s2=s3;
    27. ans=min(ans,f(s1,s2,s3));
    28. ans=min(ans,f(s1,s3,s2));
    29. ans=min(ans,f(s2,s1,s3));
    30. ans=min(ans,f(s2,s3,s1));
    31. ans=min(ans,f(s3,s2,s1));
    32. ans=min(ans,f(s3,s1,s2));
    33. cout<
    34. return 0;
    35. }

            

            

    找四边形

    不要见到图就想着跑图啊,这个题其实只是让你走两步而已(你跑那么多步干嘛

    还有一点是不要想着去模拟,仔细想想答案肯定很大的,模拟出来一个就ans++是一定不行的。

    应该换一种更快的方式去统计答案,其实不难想象到a到d很可能是有很多条路径的,那么这个路径中的任意两个组合都是一种答案。即C(n,2)种

    所以就转化成了我们求a点走两步能到哪些点,b点走两步能到哪些点,c点……然后全部加起来

    1. #include
    2. using namespace std;
    3. const int N=3e3+7;
    4. vector<int>ve[N];
    5. int n,m,sum[N][N],ans;
    6. int main(){
    7. cin>>n>>m;int u,v;
    8. for(int i=1;i<=m;i++){
    9. cin>>u>>v;ve[u].push_back(v);
    10. }
    11. for(int i=1;i<=n;i++)
    12. for(int j: ve[i])//i的孩子
    13. for(int k: ve[j])//i的孩子的孩子
    14. if(k!=i)sum[i][k]++;
    15. for(int i=1;i<=n;i++)
    16. for(int j=1;j<=n;j++)
    17. ans+=sum[i][j]*(sum[i][j]-1)/2;//求C(n,2)
    18. cout<
    19. return 0;
    20. }

            

            

    寻找正多边形

    不难想到正多边形的顶点肯定是要等差的,然后枚举每个起点开始的正三边形,正四边形,正五边形……是否存在

    1. #include
    2. using namespace std;
    3. const int N=1e5+7;
    4. int n,a[N];
    5. bool check(int s,int step){
    6. for(int i=s;i<=n;i+=step){
    7. if(a[i]==0)return false;
    8. }
    9. return true;
    10. }
    11. int main(){
    12. cin>>n;
    13. for(int i=1;i<=n;i++) cin>>a[i];
    14. for(int i=3;i<=n;i++){
    15. if(n%i)continue;//这种情况根本不存在这样的多边形
    16. int step=n/i;
    17. for(int s=1;s
    18. if(check(s,step)){
    19. cout<<"YES";return 0;
    20. }
    21. }
    22. }cout<<"NO";
    23. return 0;
    24. }

            

             

    数字串解密

    观察规律,怎么加过来的,怎么减回去。

    最开始39和-15:x1+x2=39  x1-x2=-15 所以x1是12  ,x2是27

    从而得出x1=(a+b)/2   x2=(a-b)/2。

    从而完成整个推导:

    1. #include
    2. using namespace std;
    3. const int N=260;
    4. int n,cnt,a[N],b[N];
    5. int main(){
    6. while(cin>>n,n){
    7. for(int i=0;i>a[i];
    8. for(int i=1;i!=n;i*=2){
    9. cnt=0;
    10. for(int j=0;j
    11. b[cnt++]=(a[j]+a[j+i])/2;
    12. b[cnt++]=(a[j]-a[j+i])/2;
    13. }
    14. memcpy(a,b,cnt*sizeof(int));//这里千万别写sizeof
    15. }
    16. for(int i=0;i-1;i++) cout<" ";
    17. cout<-1]<<'\n';
    18. }
    19. return 0;
    20. }

            

             

    芯片刻蚀

    思路就是模拟这个过程。

    怎么模拟呢?首先要明白这个蚀刻的矩形大小是固定的,但是我们并不确定这个矩形的最佳大小。

    我们我们对每个大小的矩形全部枚举一下,看一下当前的矩形能不能恰好完成蚀刻任务,如果能的话需要多少次?

    其实如果当前矩形是合法的(可以蚀刻出整个芯片),那么这个次数可以由总蚀刻值,除以矩形的面积直接算出,所以如果这个次数不是一个整数,那么当前就不用考虑当前的矩形了(它一定是不合法的)。

    然后还有可以优化的一点是:

    我们要的是最少的次数,必然是矩形面积越大,次数越少,那么我们就考虑面积从大到小来跑,只要发现当前的矩形是合法的就可以直接输出答案,剩下的就不用再跑了。

    1. #include
    2. using namespace std;
    3. const int N=107;
    4. int n,m,sum,ans,ma[N][N],ma2[N][N];
    5. int check(int l,int w){
    6. for(int i=1;i<=n-l+1;i++)
    7. for(int j=1;j<=m-w+1;j++){
    8. int num=ma2[i][j];
    9. for(int x=0;x
    10. for(int y=0;y
    11. int xx=i+x,yy=j+y;
    12. ma2[xx][yy]-=num;
    13. if(ma2[xx][yy]<0)return 0;
    14. }
    15. }
    16. for(int i=1;i<=n;i++)
    17. for(int j=1;j<=m;j++){
    18. if(ma2[i][j]!=0)return 0;
    19. }
    20. return 1;
    21. }
    22. int main(){
    23. cin>>n>>m;
    24. for(int i=1;i<=n;i++)
    25. for(int j=1;j<=m;j++){
    26. cin>>ma[i][j];sum+=ma[i][j];
    27. }
    28. for(int l=n;l>=1;l--)//倒着找最优解,找到就结束程序
    29. for(int w=m;w>=1;w--){
    30. if(sum%(l*w)!=0)continue;
    31. memcpy(ma2,ma,sizeof(ma));
    32. if(check(l,w)){
    33. ans=sum/(l*w);
    34. cout<return 0;
    35. }
    36. }
    37. return 0;
    38. }

            

            

    骑士锦标赛

    其实求最少的比赛天数,我们肯定是要把那些能在一天内比赛的人都在一天内完成。什么样的人可以在同一天内比赛呢?题上说——每人每天进行一场比赛,说明不同的人可以在同一天内比赛,这是要解决的第一个问题。

    另一个问题是要满足每个人的挑战顺序,这个也容易实现;对于i人能不能在今天去比赛,取决于i的对手的下一个对手是不是i,如果是的话,i就可以在今天和其对手比赛,否则就不能。

    1. #include
    2. using namespace std;
    3. const int N=1e3+7;
    4. vector<int>ve[N];
    5. int n,vis[N],id[N];//id是每个人的指针
    6. int main(){
    7. cin>>n;int x;
    8. for(int i=1;i<=n;i++)
    9. for(int j=1;j<=n-1;j++){
    10. cin>>x;ve[i].push_back(x);
    11. }
    12. int ans=0;
    13. while(1){
    14. memset(vis,0,sizeof(vis));int f=0;
    15. for(int i=1;i<=n;i++){
    16. if(vis[i]||id[i]==n-1) continue;
    17. int j=ve[i][id[i]];
    18. if(vis[j]||id[j]==n-1||ve[j][id[j]]!=i)continue;
    19. f=1;vis[i]=1;vis[j]=1;
    20. id[i]++;id[j]++;
    21. }
    22. if(!f)break;
    23. else ans++;
    24. }
    25. int f=0;
    26. for(int i=1;i<=n;i++){
    27. if(id[i]!=n-1){
    28. f=1;break;
    29. }
    30. }
    31. if(f)ans=-1;
    32. cout<
    33. return 0;
    34. }

            

            

    序列加法

    一看就是在线的做法。直接模拟着处理每个操作对所有点的影响的话是很头疼的事情,不过我们可以考虑之前每次操作对某个点的影响:

    对于操作1,既然下标是x的倍数的数都有影响,那就不妨把这些影响都保存下来,然后在操作2中对于要输出的y坐标,直接看一下之前的操作改变的下标有那些是y的因数,是的话就直接加上!

    那么就转化成了求某个数的因数,可以采用O(根号N)去枚举出来。不过要小心平方数,只能加一次哦 

    1. #include
    2. using namespace std;
    3. const int N=1e6+7;
    4. int a[N],n,m,t[N];
    5. int main(){
    6. cin>>n>>m;
    7. for(int i=1;i<=n;i++)cin>>a[i];
    8. while(m--){
    9. int num;cin>>num;
    10. if(num==1){
    11. int x,y;cin>>x>>y;t[x]+=y;
    12. }
    13. if(num==2){
    14. int i;cin>>i;
    15. int ans=a[i];
    16. for(int j=1;j<=i/j;j++){
    17. if(i%j==0){
    18. if(j*j==i)ans+=t[j];
    19. else ans+=t[j]+t[i/j];
    20. }
    21. }
    22. cout<'\n';
    23. }
    24. }
    25. return 0;
    26. }

  • 相关阅读:
    2024年06月在线IDE流行度最新排名
    VBA-自定义面板,使用SQL查询Excel数据
    Java IO包之Reader和Writer简介说明
    安全狗助力厦门“单一窗口”开展网络安全应急演练
    电吉他学习笔记
    百度松果菁英班——机器学习实践一:海量文件遍历
    我的创作纪念日(三周年)
    解决mvn常用指令build failure的问题
    【GCN基础学习】GCN基本模型概述,图卷积的基本计算方法,邻接矩阵的变换,GCN变换原理
    HTML+CSS简单的网页制作期末作业 关于我的家乡——四川文化网页介绍 DW大学生网页作业制作设计 Dreamweaver简单网页成品
  • 原文地址:https://blog.csdn.net/m0_69478376/article/details/140340872