牛客练习赛13题目题解

A-幸运数字I

/*******************************************************
Data: 2017/3/16 19:19:10
Author: Wen Yaxin

解题思路:水题,统计字符'4'和字符'7'的个数即可。

变量含义:
str:存放输入的字符串
**********************************************************/
#include 
#include 
#include 

using namespace std;

char str[100];
int main() {
    while(~scanf("%s",str)) {
        int four = 0;
        int seven = 0;
        for(int i = 0; i < (int)strlen(str); i++) {
            if(str[i]=='4') {
                four++;
            }
            if(str[i]=='7') {
                seven++;
            }
        }
        if(four==0 && seven==0) {
            printf("-1\n");
        }
        else {
            if(four>=seven) {
                printf("4\n");
            }
            else {
                printf("7\n");
            }
        }
    }
    return 0;
}

B-幸运数字II

/*******************************************************************************
Data: 2018-03-16 20:11:44
Author: Wen Yaxin

解题思路:首先使用dfs求解出所有的幸运数字,题目给出
数字数据范围为10亿,则最多10位由4和7组成的数字就是
极限,对于每一位,可以是4,也可以是7,用递归进行计算,
并保留所有的中间结果,一次递归的过程求出所有幸运数字。
大概有一千多个,然后对他们进行排序,则对于给出的L,R,
可以知道两个相邻幸运数字间的那些数字都对答案贡献较小
的那个幸运数字,就这样跳着求。只要求出大于等于L幸运数
字的位置,之后便不用查找,通过自增得来就可以了。


变量含义:
cnt:t数组的变量,在存储幸运数字时使用
t:t数组用来存储所有的幸运数字

方法含义:
dfs:递归求幸运数字,dfs(x,num)x代表当前这位是x
getNext:使用二分查找第一个大于等于i的幸运数字的位置
***************************************************************************/
#include 
#include 
#include 
#include 

using namespace std;

const long long maxn = 4444444444;
long long cnt,t[1000];
void dfs(int x,long long num) {
   num = num*10 + x;
   if(num > maxn) {
        return;
   }
   t[cnt++] = num;  //存储中间的每一个数
   dfs(4,num);
   dfs(7,num);
}
int getNext(long long i){
    int pos = lower_bound(t,t+cnt,i)-t;
    return pos;
}
int main() {
    cnt = 0;
    dfs(4,(long long)0);
    dfs(7,(long long)0);
    //printf("%d\n",cnt);
    sort(t,t+cnt); //排序,二分查找要求数组元素有序
    long long L,R,temp;
    while(~scanf("%lld%lld",&L,&R)) {
        //防止L>R的情况,但本题此判断无用,因为题目说了L<=R
        if(L>R) {
            temp = L;
            L = R;
            R = temp;
        }
        long long ans = 0;
        long long i = L;
        long long x;
        long long j = getNext(i);  //求起始位置。
        while(true) {
            x = t[j];
            if(x >= R) {
                ans += (R-i+1)*x;
                break;
            }
            ans += (x-i+1)*x;
            i = x+1;
            j++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

C-幸运数字III

/**********************************************************************
Data: 2018-03-16 21:15:54
Author: Wen Yaxin

解题思路:题目K很大,一次次的变换,对于题目给出的1秒时间显然是
不可能过的。则根据题目分析如下。
1.最早出现47的地方,如果47后面有7,则此时出现序列477,如果4是第
奇数个字符,则一次变换后,序列为447,再次出现47,4是第偶数个字符,
变换后变成477。由此可知,出现477(且其里面的47是串里面最左侧的47),
对于剩余变换次数,如果是奇数,最终中间位为4,否则不变,遇到这种情况
不论还有多少次变换,只要确定变换次数的奇偶即可确定答案。

2.最早出现47的地方,如果47前面有4,则此时出现序列447,如果47中的4
是第偶数位字符,则变换一次,序列变成477,则再次变换一次,序列变成447.
可以发现,出现447(且其里面的47是串里面最左侧的47),对于剩余变换次数,
是奇数次,则中间的4最终为7,否则不变。遇到这种情况不论还有多少次变换,
只要确定变换次数的奇偶即可确定答案。

3.不是1,2两种情况,只是单纯找到了47,如果4是第奇数个字符,则47 -> 44.
下一次考虑47就要从后一个4开始检测了。

4.如果4是第偶数个字符,则47->77,则检测位置需要从4左侧的位置开始。

5.当串中没有47的时候,无论还剩余多少次变换都是无用的,应立即结束。


变量含义:
str:存放题目给出的字符串
******************************************************************************/
#include 
#include 
#include 

using namespace std;

int n,k,temp;
char str[1000000];
int main() {
    while(~scanf("%d%d",&n,&k)) {
        scanf("%s",str);
        //变换0次,直接输出。
        if(k == 0) {
           puts(str);
        }
        else {
            for(int i = 0; i < n-1; i++) {
                //遇到47组合。
                if(str[i]=='4' && str[i+1]=='7') {
                    //遇到477组合
                    if(i+2=0 && str[i-1]=='4') {
                        temp = i+1;
                        //中间的4是第偶数个字符
                        if(temp%2==0) {
                            if(k%2) {
                                str[i] = '7';
                            }
                            k = 0;
                        }
                    }
                    if(!k) break;
                    temp = i+1;
                    //是第奇数个字符。
                    if(temp%2) {
                        str[i+1] = '4';
                        k--;    //剩余变换次数-1
                    }
                    else {
                        str[i] = '7';
                        k--;
                        i = i-2;
                        /*i=i-2是因为,下次循环位置要从i-1开始,
                        由于for循环中i会自增,所有让i减去2,再
                        自增1,即下次循环从i-1开始。*/
                    }
                    if(!k) break;
                }
            }
            puts(str);
        }
    }
    return 0;
}

D-幸运数字IV

/*******************************************************************************************
Date 2018/3/17 17:47
Author Wen Yaxin

解题思路:1~n,n个数的全排列有n!种。13的阶乘已经大于10亿。
10亿是题中给出最大的数据.
1.如果第k小全排列不存在,直接输出-1。
2.则题目转换成求:[max(1,n-12),n]的第k小全排列。
则对于max(1,n-12)-1>=1的前提下,我们求1~max(1,n-12)-1中有
多少个luckyNumer,然后在看[max(1,n-12),n]种有多少个luckyNumer
其下标也是luckyNumer。两个值相加。

常量含义:
maxn:大于10亿的第一个幸运数字

变量含义:
fac:fac[i]存放i的阶乘
n,k:题目给出的数据放在n,k
t:用来存储所有的幸运个数字
arr:用来存放需要求全排列的序列
b:arr数组得第k小排列,存放在b中
cnt:作为t数组的下标
Start:起始数字
End:终止数字

方法含义:
calFac:计算阶乘
dfs(int x,long long num):递归求解所有幸运数字,x代表当前位取4还是取7,num是上一步的值
fun(long long x):该方法查找t数组并返回第一个大于等于x的幸运数字在t数组的下标
init:完成初始化工作
kth_permuation(rest,le,num):求arr数组中存放序列的第k小全排列。rest是剩余多少个数字。
le每确定一个位置上的数字,le--.num第num小排列。
isLuckyNum:判断某个数字是否幸运数字

测试数据:
输入:
100000000 100000
95729374 39579283745
10 10000000
10 1324346
9357 35738945
1000000000 1000000000
1324324 54678574
44774480 464817709
输出:
510
-1
-1
0
30
1022
126
306
***************************************************************************************/
#include 
#include 
#include 

using namespace std;

const long long maxn = 4444444444;
long long fac[20],n,k,t[2000],arr[20],b[20];
int cnt,len;
long long Start,End;
//计算阶乘
void calFac() {
    fac[0] = 1;
    for(int i = 1; i <= 14; i++) {
        fac[i] = fac[i-1]*i;
    }
}
void dfs(int x,long long num) {
    num = num*10 + x;
    if(num > maxn) {
        return;
    }
    t[cnt++] = num;
    dfs(4,num);
    dfs(7,num);
}
int fun(long long x) {
    int pos = lower_bound(t,t+cnt,x)-t;
    return pos;
}
void init() {
    calFac();
    cnt = 0;
    dfs(4,(long long)0);
    dfs(7,(long long)0);
    sort(t,t+cnt);
}
//求第k小全排列。
void kth_permuation(int rest,int le,long long num) {
    int pos = 0;
    while(le) {
        for(int i = 0; i < len; i++) {
            if(num <= fac[rest]) {
                b[pos++] = arr[i];
                arr[i] = maxn;
                rest--;
                sort(arr,arr+le);
                break;
            }
            else {
                 num = num-fac[rest];
            }
        }
        le--;
    }
}
//判断是否luckyNum
bool isLuckyNum(long long num) {
    while(num) {
        if(num%10!=4 && num%10!=7) return false;
        num = num/(long long)10;
    }
    return true;
}
int main() {
    init();
    while(~scanf("%lld%lld",&n,&k)) {
        int temp = min(n,(long long)13);
        if(k > fac[temp]) {
            printf("-1\n");
        }
        else {
            End = n;
            Start = max((long long)1,End-12);
            len = End-Start+1;
            for(int i = 0; i < len; i++) {
                arr[i] = Start+(long long)i;
            }
            kth_permuation(len-1,len,k);
            int ans = 0;
            temp = fun(Start-1);
            if(t[temp]==Start-1) {
                ans = temp+1;
            }
            else {
                ans = temp;
            }
            for(int i = 0; i < len; i++) {
                if(isLuckyNum(b[i])) {
                   if(isLuckyNum(Start+i)) {
                        ans++;
                   }
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}



E-乌龟跑步

/********************************************************************************
Date 2018/3/18 11:17
Author Wen Yaxin

解题思路:这个题目是4维的DP,没想出来,看别人的思路。
dp[i][j][k][status]  前i个字符,改变j次,是否能到达k位置,且其朝向是status。
**********************************************************************************/
#include 
#include 
#include 
#include 
#include 

using namespace std;

int dp[110][55][220][5];  //dp[i][j][k][status] 前i个字符,改变j次,是否能到达距源点k的地方,并且朝向是status.
char str[120];
int main() {
    int n;
    while(~scanf("%s",str+1)) {
        scanf("%d",&n);
        int len = strlen(str+1);
        memset(dp,0,sizeof(dp));
        dp[0][0][100][1] = 1;  //把负坐标轴抵消掉,则源点放在x=100的位置,
        for(int i = 1; i <= len; i++) {
            for(int j = 0; j <= n; j++) {
                for(int k = 0; k <= 200; k++) {
                    //注意下面的运算都是或运算。a |= b -> a = a|b
                    //当前指令是向前走
                    if(str[i]=='F') {
                        //还有转换次数,F->T,不讨论朝向,两个方向均考虑
                        if(j) dp[i][j][k][1] |= dp[i-1][j-1][k][0]; //右转左
                        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];
                    }
                    else {
                        //T->F,反转变成直走
                        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];
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 0; i <= 200; i++) {
            if(dp[len][n][i][1]) ans = max(ans,abs(100-i));
            if(dp[len][n][i][0]) ans = max(ans,abs(100-i));
        }
        printf("%d\n",ans);
    }
    return 0;
}


F-m皇后

/*********************************************************************************
Date 2018/3/18 10:22
Author Wen Yaxin

解题思路:存储一个点的横纵坐标x,y以及x-y,x+y
的值。对于x,y,我们可以由此判断皇后们是否处于同行或同列。
而x-y和x+y可以判断皇后是否处于同一斜线。
如果皇后(x1,y1) (x2,y2)处于同一斜线。

则斜率可以是1,也可以是-1.(题目说了不会有两个皇后在同一位置)
(1)斜率为1:y2-y1/(x2-x1) = 1  --> y2-y1=x2-x1 --> x1-y1 = x2-y2
所以对于x1-y1 = x2-y2的点,可以判断他们处于西南-东北方向的斜线上

(2)斜率为-1:y2-y1/(x2-x1) = -1 --> y2-y1=x1-x2 --> x1+y1 = x2+y2
所欲对于x1+y1 = x2+y2的点,可以判断他们处于西北-东南方向的斜线上。 

变量含义:
node:维护皇后的横坐标,纵坐标,横纵坐标和,横纵坐标差,和不安全方向数

方法含义:
cmp1:按照横坐标排序,横坐标相同时使纵坐标从小到大排序,用来处理处于同一行的皇后
cmp2:按照纵坐标排序,纵坐标相同时按横坐标从低到高排序,用来处理处于同一列的皇后
cmp3:按照x-y排序,x-y相同时,按x+y从小到大排序,用来处理从西南->东北方向的皇后
cmp4:按照x+y排序,x+y相同时,按x-y从小到大排序,用来处理从西北->东南方向的皇后
*********************************************************************************/
#include 
#include 
#include 
#include 

using namespace std;

const int maxn = 1e5+10;
int ans[10];
struct Node {
    int r;    //行号
    int c;    //列号
    int p;
    int q;
    int num;
}node[maxn];
//使同行的皇后,按照纵坐标从小到大排列
bool cmp1(Node a,Node b) {
    if(a.r == b.r) return a.c < b.c;
    return a.r < b.r;
}
//使同列的皇后,按照横坐标从小到大排列
bool cmp2(Node a,Node b) {
    if(a.c == b.c) return a.r < b.r;
    return a.c < b.c;
}
//使处于东北-西南方向的皇后,
bool cmp3(Node a,Node b) {
    if(a.p == b.p) return a.q

你可能感兴趣的:(ACM_比赛题解)