牛客编程巅峰赛S1第2场 - 青铜&白银

A-牛牛扔牌
链接:https://ac.nowcoder.com/acm/contest/6219/A
来源:牛客网

牛牛现在有n张扑克牌,每张扑克牌都有点数和花色两部分组成。点数为‘1’-‘9’的正整数,花色为’C’,‘D’,‘H’,‘S’'其中的一个,分别表示梅花、方块、红桃、黑桃。现在牛牛想按一定的顺序把这n张牌扔掉。扔牌顺序的规则如下1.:
1.如果现在还剩素数张牌,则将牌顶的牌扔掉
2.如果现在还剩非素数张牌,则将牌底的牌扔掉
牛牛想知道他的扔牌顺序是什么,请返回扔牌顺序的字符串
示例一
输入:

"3C8D6H3D"

输出:

"3D3C8D6H"

说明

开始n=4,为非素数,扔掉牌底的牌3D n=3,为素数,扔掉牌顶的牌3C n=2,为素数,扔掉牌顶的牌8D
n=1,为非素数,扔掉牌底的牌6H

示例二
输入

"8S8S8S8S8S8S8S"

输出

"8S8S8S8S8S8S8S"

说明

因为全是8S,所以扔牌顺序的每一张牌也都是8S

备注

对于100%的数据,1≤n≤10

c++
素数判断+模拟
要注意的点是两个字符为一张牌

class Solution
{
public:
    /**
     *
     * @param x string字符串 字符串从前到后分别是从上到下排列的n张扑克牌
     * @return string字符串
     */
    string Orderofpoker(string x)
    {
        // write code here
        int l=x.size();
        string s;
        int ll=0,rr=l-1;
        int xx=l/2;
        while(ll<rr)
        {
            
            int f=1;
            for(int i=2; i*i<=xx; i++)
            {
                if(xx%i==0)
                {
                    f=0;
                    break;
                }
            }
            if(f)
            {
                s+=x[ll];
                s+=x[ll+1];
                ll+=2;
            }
            else
            {
                s+=x[rr-1];
                s+=x[rr];
                rr-=2;
            }
            xx--;
        }
        return s;
    }
};

B-疯狂过山车
链接:https://ac.nowcoder.com/acm/contest/6219/B
来源:牛客网

今天牛牛去游乐园玩过山车项目,他觉得过山车在上坡下坡的过程是非常刺激的,回到家之后就受到启发,想到了一个问题。如果把整个过山车的轨道当作是一个长度为n的数组num,那么在过山车上坡时数组中的值是呈现递增趋势的,到了最高点以后,数组中的值呈现递减的趋势,牛牛把符合这样先增后减规律的数组定义为金字塔数组,请你帮牛牛在整个num数组中找出长度最长的金字塔数组,如果金字塔数组不存在,请输出0。
输入

4,[1,2,3,1]

输出

4

输入

5,[1,5,3,3,1]

输出

3

1<=n<=1000000,且num数组中的数 0<=num[i]<=1000000。

C++:
先用差分统计从头到尾上升的各个长度,再从尾到头统计上升的各个长度,最后遍历max(res, f[i]+g[i]+1)

class Solution
{
public:
    /**
     *
     * @param n int整型
     * @param num int整型vector
     * @return int整型
     */
     int f[1000005],g[1000005];
     int getMaxLength(int n, vector<int>& num) {
        // write code here

        f[0] = 0;
        for(int i=1; i<n; i++){
            if(num[i] > num[i-1]) f[i] = f[i-1]+1;
            else f[i] = 0;
        }
        g[n-1] = 0;
        for(int i=n-2; i>=0; i--){
            if(num[i]>num[i+1]) g[i] = g[i+1] + 1;
            else g[i] = 0;
        }
        int res = 0;
        for(int i=0; i<n; i++)
            res = max(res, f[i]+g[i]+1);
        return res;
    }
};

C-牛牛的棋盘
链接:https://ac.nowcoder.com/acm/contest/6219/C
来源:牛客网

牛牛最近在家里看到一个棋盘,有nm个格子,在棋盘旁边还放着k颗棋子,牛牛想把这k颗棋子全部放在nm的棋盘上,但是有一个限制条件:棋盘的第一行、第一列、最后一行和最后一列都必须有棋子。牛牛想知道这样的棋子放法到底有多少种,答案需要对1e9+7取模。
输入

2,3,1

输出

0

说明

就1颗棋子,所以无法满足条件。

输入

2,2,2

输出

2

我们可以把第1颗棋子放在左上角,第2颗棋子放在右下角;也可以把第1颗棋子放在右上角,第2颗棋子放在左下角。故而有2种放法。

备注

2<=n,m<=30; 1<=k<=1000

运用了容斥定理和费马小定理以及状态压缩
通过费马小定理输出所有组合数的可能

    for(int i=0;i<=n;++i) dp[i][0]=dp[i][i]=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<i;++j)
            dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;

输出结果如下:
在这里插入图片描述

例如dp[4][2]就代表 C 4 2 C_{4}^{2} C42,dp[4][1]就代表 C 4 1 C_{4}^{1} C41,以此类推;

然后去推测他的可能性有多少种:
k个棋子放在n*m上的方案数为: C n m k C_{nm}^{k} Cnmk
第一行或者最后一行没有放棋子的方案数为: C ( n − 1 ) m k C_{(n-1)m}^{k} Cn1mk
第一列或者最后一列没有放棋子的方案数为: C n ( m − 1 ) k C_{n(m-1)}^{k} Cnm1k
最后一行和最后一行都没有放: C ( n − 2 ) m k C_{(n-2)m}^{k} Cn2mk
最后一列和最后一列都没有放: C n ( m − 2 ) k C_{n(m-2)}^{k} Cnm2k
某一列和某一行没有放 C ( n − 1 ) ( m − 1 ) k C_{(n-1)(m-1) }^{k} C(n1)(m1)k
某两列和某一行没有放 C ( n − 1 ) ( m − 2 ) k C_{(n-1)(m-2) }^{k} C(n1)(m2)k
某一列和某两行没有放 C ( n − 2 ) ( m − 1 ) k C_{(n-2)(m-1) }^{k} C(n2)(m1)k
都没有放 C ( n − 2 ) ( m − 2 ) k C_{(n-2)(m-2) }^{k} C(n2)(m2)k

用状态压缩或者直接遍历所有可能性

	for(int i=0;i<16;i++)//状压
	{
		int c=(i&1)+(i&2);
		int h=(i&4)+(i&8);
		int x=c+h;
		if(x&1)
		  ans=(ans-dp[(n-c)*(m-h)][k]+mod)%mod;//总方案-奇数没放的剩下的就是放的
		else
		  ans=(ans+dp[(n-c)*(m-h)][k]+mod)%mod;//偶数个要加回来
	}

总和的式子为: C n m k C_{nm}^{k} Cnmk- C n ( m − 1 ) k C_{n(m-1)}^{k} Cnm1k- C n ( m − 1 ) k C_{n(m-1)}^{k} Cnm1k+ C n ( m − 2 ) k C_{n(m-2)}^{k} Cnm2k- C ( n − 1 ) m k C_{(n-1)m}^{k} Cn1mk+ C ( n − 1 ) ( m − 1 ) k C_{(n-1)(m-1)}^{k} Cn1m1k+ C ( n − 1 ) ( m − 1 ) k C_{(n-1)(m-1)}^{k} Cn1m1k- C ( n − 1 ) ( m − 2 ) k C_{(n-1)(m-2)}^{k} Cn1m2k-
C ( n − 1 ) m k C_{(n-1)m}^{k} Cn1mk+ C ( n − 1 ) ( m − 1 ) k C_{(n-1)(m-1)}^{k} Cn1m1k+ C ( n − 1 ) ( m − 1 ) k C_{(n-1)(m-1)}^{k} Cn1m1k- C ( n − 1 ) ( m − 2 ) k C_{(n-1)(m-2)}^{k} Cn1m2k+ C ( n − 2 ) m k C_{(n-2)m}^{k} Cn2mk- C ( n − 2 ) ( m − 1 ) k C_{(n-2)(m-1)}^{k} Cn2m1k- C ( n − 2 ) ( m − 1 ) k C_{(n-2)(m-1)}^{k} Cn2m1k+ C ( n − 2 ) ( m − 2 ) k C_{(n-2)(m-2)}^{k} Cn2m2k

C++:(状态压缩)

class Solution {
public:
    int mod=1e9+7;
    long long f[1500][1500];
    void init()//打组合数表
    {
      for(int i=0;i<1500;i++)
	  {
		f[i][i]=1;
		f[i][0]=1;
	  }
	  for(int i=1;i<1500;i++)
	    for(int j=1;j<i;j++)
		  f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
    }
    int solve(int n, int m, int k) {
        // write code here
	init();
	long long ans=0;
	for(int i=0;i<16;i++)//状压
	{
		int c=(i&1)+((i>>1)&1);
		int h=((i>>2)&1)+((i>>3)&1);
		int x=c+h;
		if(x&1)
		  ans=(ans-f[(n-c)*(m-h)][k]+mod)%mod;
		else
		  ans=(ans+f[(n-c)*(m-h)][k]+mod)%mod;
	}
        return ans;
    }
};

C++:(直接模拟)

class Solution {
public:
    /**
     * @param n int整型 
     * @param m int整型 
     * @param k int整型 
     * @return int整型
     */
    long long dp[2007][2007];
    long long mod=1e9+7;
    void Clear(int n){
    for(int i=0;i<=n;++i) dp[i][0]=dp[i][i]=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<i;++j)
            dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;
    }
    int solve(int n, int m, int k) {
        // write code here
        Clear(n*m);
        long long res=0;
        for(int i=0;i<=1;++i)
            for(int j=0;j<=1;++j)
                for(int x=0;x<=1;++x)
                    for(int y=0;y<=1;++y){
                        int c=i+j;
                        int h=x+y;
                        int x=c+h;
                        if(x%2==1) res=(res-dp[(n-c)*(m-h)][k]+mod)%mod;
                        else res=(res+dp[(n-c)*(m-h)][k]+mod)%mod;
                    }
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值