牛客练习赛13

A:幸运数字Ⅰ

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在,给定一个字符串s,请求出一个字符串,使得:
1、它所代表的整数是一个幸运数字;
2、它非空;
3、它作为s的子串(不是子序列)出现了最多的次数(不能为0次)。
请求出这个串(如果有多解,请输出字典序最小的那一个)。

输入描述:

串s(1 <= |s| <= 50)。s只包含数字字符,可以有前导零。

输出描述:

一个串表示答案。
无解输出-1。

输入

047

输出

4
题目分析:答案肯定是4 or 7 or -1 想一想要想子字符串出现次数最多,肯定是单个的嘛

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;

string str;

int main()
{
    cin>>str;
    int ans4=0,ans7=0;
    for (int i=0;i<str.size();i++) {
        if (str[i]=='4') ans4++;
        if (str[i]=='7') ans7++;
    }
    if (ans4||ans7) {
        if (ans4>=ans7) printf("%d\n",4);
        else if (ans4<ans7) printf("%d\n",7);
    }
    else {
        printf("%d\n",-1);
    }
    return 0;
}


B:幸运数字Ⅱ

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
定义next(x)为大于等于x的第一个幸运数字。给定l,r,请求出next(l) + next(l + 1) + ... + next(r - 1) + next(r)。

输入描述:

两个整数l和r (1 <= l <= r <= 1000,000,000)。

输出描述:

一个数字表示答案。

输入

2 7

输出

33
题目分析:left和right的区间非常大,暴力这个区间不可取,但是这样的幸运数字确不多咯,所有我们枚举幸运数字,然后算出每个幸运数字的贡献(使用了多少次)

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
#define ll unsigned long long
using namespace std;
const ll inf=1e12;
vector<ll> vec;
void dfs(int len,ll sum)
{
    if (sum>inf) return ;
    vec.push_back(sum);
    dfs(len+1,sum*10+4);
    dfs(len+1,sum*10+7);
}
int main()
{
    dfs(0,(ll) 0);
    sort(vec.begin(),vec.end());
    ll left,right;
   cin>>left>>right;
    ll ans=0;
    for (int i=1;i<vec.size();i++) {
        if (left>right) break;
        if (vec[i]>=left) {
            if (vec[i]<=right) {
               ans+=(vec[i]-left+1)*vec[i];
                left=vec[i]+1;
            }
            else {
                ans+=(right-left+1)*vec[i];
                left=right+1;
            }
        }
    }
    cout<<ans;
}

C:幸运数字Ⅲ

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
假设现在有一个数字d,现在想在d上重复k次操作。
假设d有n位,用d1,d2,...,dn表示。
对于每次操作,我们想要找到最小的x (x < n),使得dx=4并且dx+1=7。
如果x为奇数,那么我们把dx和dx+1都变成4;
否则,如果x为偶数,我们把dx和dx+1都变成7;
如果不存在x,那么我们不做任何修改。
现在请问k次操作以后,d会变成什么样子。

输入描述:

第一行两个整数n,k表示d的长度和操作次数。
第二行一个数表示d。数据保证不存在前导零。
1 <= n <= 100,000
0 <= k <= 1000,000,000

输出描述:

一个数字表示答案。

输入

7 4
4727447

输出

4427477

题目分析:

感谢评论席里的大佬指出错误。

这题存在俩个循环:

1:当出现447时,如果第二个4的位置是偶数,那么会变成477,若k不为0,那么接着又会变成447

2:   当出现477时,如果第一个4的位置是奇数,那么会变成447,若k不为0,那么接着又会变成477

如果出现这样的循环就需要根据当前k的值直接输出结果了

代码如下:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+100;
char str[maxn];

int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    scanf("%s",str+1);
    for (int i=1;i<=n-1;i++) {
        if (k==0) break;
        if (str[i]=='4'&&str[i+1]=='7') {
            if (i%2==0) {
                if (i!=1&&str[i-1]=='4') {//447的情况
                   if (k%2==1) {
                     str[i]=str[i+1]='7';
                     printf("%s\n",str+1);
                     return 0;
                   }
                   else {
                     printf("%s\n",str+1);
                     return 0;
                   }
                }
                else {
                    k--;
                    str[i]=str[i+1]='7';
                }
            }
            else {
                if (i<=n-2&&str[i+2]=='7') {//477的情况
                    if (k%2==1) {
                       str[i]=str[i+1]='4';
                       printf("%s\n",str+1);
                       return 0;
                    }
                    else {
                     printf("%s\n",str+1);
                     return 0;
                   }
                }
                else {
                    k--;
                    str[i]=str[i+1]='4';
                }
            }
        }
    }
    printf("%s\n",str+1);
    return 0;
}


E:
乌龟跑步

有一只乌龟,初始在0的位置向右跑。
这只乌龟会依次接到一串指令,指令T表示向后转,指令F表示向前移动一个单位。乌龟不能忽视任何指令。
现在我们要修改其中正好n个指令(一个指令可以被改多次,一次修改定义为把某一个T变成F或把某一个F变成T)。
求这只乌龟在结束的时候离起点的最远距离。(假设乌龟最后的位置为x,我们想要abs(x)最大,输出最大的abs(x)

输入描述:

第一行一个字符串c表示指令串。c只由F和T构成。
第二行一个整数n。
1 <= |c| <= 100, 1 <= n <= 50

输出描述:

一个数字表示答案。

输入

FT
1

输出

2
题目分析:这个就是DP了,dp[i][j][k][status] 表示前面i个字符,转化j次 status表示方向0左1右 能否到达位置k

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

int dp[110][55][220][2];
char str[110];

int main()
{
    scanf("%s",str+1);
    int n=strlen(str+1);
    int m;
    scanf("%d",&m);
    memset (dp,0,sizeof (dp));
    dp[0][0][100][1]=1;//假定位置在100
    for (int i=1;i<=n;i++) {
        for (int j=0;j<=m;j++) {
            for (int k=0;k<=200;k++) {
                if (str[i]=='F') {
                    if (j) dp[i][j][k][1]|=dp[i-1][j-1][k][0];//F变T 转向
                    if (j) dp[i][j][k][0]|=dp[i-1][j-1][k][1];
                    dp[i][j][k+1][1]|=dp[i-1][j][k][1];//前进
                    dp[i][j][k-1][0]|=dp[i-1][j][k][0];//后退
                    if (j>=2) dp[i][j][k+1][1]|=dp[i-1][j-2][k][1];//反转俩次不变,然后前进
                    if (j>=2) dp[i][j][k-1][0]|=dp[i-1][j-2][k][0];//后退
                }
                else {//和前面的差不多
                    if (j) dp[i][j][k+1][1]|=dp[i-1][j-1][k][1];
                    if (j) dp[i][j][k-1][0]|=dp[i-1][j-1][k][0];
                    dp[i][j][k][1]|=dp[i-1][j][k][0];
                    dp[i][j][k][0]|=dp[i-1][j][k][1];
                    if (j>=2) dp[i][j][k][1]|=dp[i-1][j-2][k][0];
                    if (j>=2) dp[i][j][k][0]|=dp[i-1][j-2][k][1];
                }
            }
        }
    }
    int ans=0;
    for (int i=0;i<=200;i++) {
        if (dp[n][m][i][1]) ans=max(ans,(int) abs(100-i));
        if (dp[n][m][i][0]) ans=max(ans,(int) abs(100-i));
    }
    printf("%d\n",ans);
}

F:
m皇后

在一个n*n的国际象棋棋盘上有m个皇后。
一个皇后可以攻击其他八个方向的皇后(上、下、左、右、左上、右上、左下、右下)。
对于某个皇后,如果某一个方向上有其他皇后,那么这个方向对她就是不安全的。
对于每个皇后,我们都能知道她在几个方向上是不安全的。

现在我们想要求出t 0,t 1,...,t 8,其中t i表示恰有i个方向是"不安全的"的皇后有多少

输入描述:

第一行两个整数n,m表示棋盘大小和皇后数量。
接下来m行每行两个整数ri,ci表示皇后坐标。
1 <= n, m <= 100,000
1 <= ri, ci <= n
数据保证没有皇后在同一个位置上。

输出描述:

一行九个整数表示答案。
空格隔开,结尾无空格

输入

8 4
4 3
4 8
6 5
1 6

输出

0 3 0 1 0 0 0 0 0

题目分析:图存不下,只能存点,搜索的话时间不允许,我们把八个方向分成4种

向上 向下  我把坐标按照横坐标相同,把纵坐标小的放在前面,否则吧横坐标小的放在前面

向左向右  我把坐标按照纵坐标相同,把横坐标小的放在前面,否则吧纵坐标小的放在前面

左上 右下 我们把纵坐标和横坐标相加的值排序,相同把横坐标小的放在前面

左下 右上 我们把横坐标和纵坐标相减的值排序,相同的横坐标小的放在前面


代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
 
struct Dian
{
    int a,b,id;
}dian[100005];
int num[100005],ans[9];
 
bool cmp1(const Dian &aa,const Dian &bb)
{
    if(aa.b!=bb.b)return aa.b<bb.b;
    return aa.a<bb.a;
}
 
bool cmp2(const Dian &aa,const Dian &bb)
{
    if(aa.a!=bb.a)return aa.a<bb.a;
    return aa.b<bb.b;
}
 
bool cmp3(const Dian &aa,const Dian &bb)
{
    if(aa.a+aa.b!=bb.a+bb.b)return aa.a+aa.b<bb.a+bb.b;
    return aa.a<bb.a;
}
 
bool cmp4(const Dian &aa,const Dian &bb)
{
    if(aa.a-aa.b!=bb.a-bb.b)return aa.a-aa.b<bb.a-bb.b;
    return aa.a<bb.a;
}
 
int main()
{
    int n,m;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&dian[i].a,&dian[i].b);
        dian[i].id=i;
    }
 
    sort(dian+1,dian+1+n,cmp1);
    for(int i=1;i<n;i++)
        if(dian[i+1].b==dian[i].b)num[dian[i].id]++;
    for(int i=n;i>1;i--)
        if(dian[i-1].b==dian[i].b)num[dian[i].id]++;
 
    sort(dian+1,dian+1+n,cmp2);
    for(int i=1;i<n;i++)
        if(dian[i+1].a==dian[i].a)num[dian[i].id]++;
    for(int i=n;i>1;i--)
        if(dian[i-1].a==dian[i].a)num[dian[i].id]++;
 
    sort(dian+1,dian+1+n,cmp3);
    for(int i=1;i<n;i++)
        if(dian[i+1].a+dian[i+1].b==dian[i].a+dian[i].b)num[dian[i].id]++;
    for(int i=n;i>1;i--)
        if(dian[i-1].a+dian[i-1].b==dian[i].a+dian[i].b)num[dian[i].id]++;
 
    sort(dian+1,dian+1+n,cmp4);
    for(int i=1;i<n;i++)
        if(dian[i+1].a-dian[i+1].b==dian[i].a-dian[i].b)num[dian[i].id]++;
    for(int i=n;i>1;i--)
        if(dian[i-1].a-dian[i-1].b==dian[i].a-dian[i].b)num[dian[i].id]++;
 
    for(int i=1;i<=n;i++)
        ans[num[i]]++;
 
    for(int i=0;i<=8;i++)
    {
        printf("%d%c",ans[i],i==8?'\n':' ');
    }
 
    return 0;
}


D题好像是什么康托展开什么的,不会!

俩个大佬博客分享大家.

康托展开

D题题解














评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值