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}
C(n−1)mk
第一列或者最后一列没有放棋子的方案数为:
C
n
(
m
−
1
)
k
C_{n(m-1)}^{k}
Cn(m−1)k
最后一行和最后一行都没有放:
C
(
n
−
2
)
m
k
C_{(n-2)m}^{k}
C(n−2)mk
最后一列和最后一列都没有放:
C
n
(
m
−
2
)
k
C_{n(m-2)}^{k}
Cn(m−2)k
某一列和某一行没有放
C
(
n
−
1
)
(
m
−
1
)
k
C_{(n-1)(m-1) }^{k}
C(n−1)(m−1)k
某两列和某一行没有放
C
(
n
−
1
)
(
m
−
2
)
k
C_{(n-1)(m-2) }^{k}
C(n−1)(m−2)k
某一列和某两行没有放
C
(
n
−
2
)
(
m
−
1
)
k
C_{(n-2)(m-1) }^{k}
C(n−2)(m−1)k
都没有放
C
(
n
−
2
)
(
m
−
2
)
k
C_{(n-2)(m-2) }^{k}
C(n−2)(m−2)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}
Cn(m−1)k-
C
n
(
m
−
1
)
k
C_{n(m-1)}^{k}
Cn(m−1)k+
C
n
(
m
−
2
)
k
C_{n(m-2)}^{k}
Cn(m−2)k-
C
(
n
−
1
)
m
k
C_{(n-1)m}^{k}
C(n−1)mk+
C
(
n
−
1
)
(
m
−
1
)
k
C_{(n-1)(m-1)}^{k}
C(n−1)(m−1)k+
C
(
n
−
1
)
(
m
−
1
)
k
C_{(n-1)(m-1)}^{k}
C(n−1)(m−1)k-
C
(
n
−
1
)
(
m
−
2
)
k
C_{(n-1)(m-2)}^{k}
C(n−1)(m−2)k-
C
(
n
−
1
)
m
k
C_{(n-1)m}^{k}
C(n−1)mk+
C
(
n
−
1
)
(
m
−
1
)
k
C_{(n-1)(m-1)}^{k}
C(n−1)(m−1)k+
C
(
n
−
1
)
(
m
−
1
)
k
C_{(n-1)(m-1)}^{k}
C(n−1)(m−1)k-
C
(
n
−
1
)
(
m
−
2
)
k
C_{(n-1)(m-2)}^{k}
C(n−1)(m−2)k+
C
(
n
−
2
)
m
k
C_{(n-2)m}^{k}
C(n−2)mk-
C
(
n
−
2
)
(
m
−
1
)
k
C_{(n-2)(m-1)}^{k}
C(n−2)(m−1)k-
C
(
n
−
2
)
(
m
−
1
)
k
C_{(n-2)(m-1)}^{k}
C(n−2)(m−1)k+
C
(
n
−
2
)
(
m
−
2
)
k
C_{(n-2)(m-2)}^{k}
C(n−2)(m−2)k
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;
}
};