腾讯暑假实习提前批A题 ******贪心
题意:
牛家村的货币是一种很神奇的连续货币。
他们货币的最大面额是n,并且一共有面额为1,面额为2…面额为n,n种面额的货币。
牛牛每次购买商品都会带上所有面额的货币,支付时会选择给出硬币数量最小的方案。
现在告诉你牛牛将要购买的商品的价格,你能算出牛牛支付的硬币数量吗? (假设牛牛每种面额的货币都拥有无限个。)
输入格式
共一行,包含两个整数n和m,分别表示货币的最大面额以及商品的价格。
输出格式
一个整数表示牛牛支付的硬币数量。
数据范围
1≤n≤105,
1≤m≤109
输入样例1:
6 7
输出样例1:
2
输入样例2:
4 10
输出样例2:
3
思路:其实就是一个简单的贪心,每次为了凑足m元钱,去[1-n]的面额里面,取最大的n面额去凑就可以使硬币数量最小。
*代码
#include <iostream>
using namespace std;
int main() {
int n, m, res;
cin >> n >> m;
res = (m + n - 1) / n;//(m/n)向上取整
cout << res << endl;
}
腾讯暑假实习提前批B题 ********规律
妞妞最近迷上了王者荣耀。
小Q得到了一个奇妙的数列,这个数列有无限多项,数列中的第 i 个数字为i∗(−1)i,比如数列的前几项为-1,2,-3,4,-5…
小Q兴奋把这个数列拿去给妞妞看,并希望借此邀请妞妞吃饭。
妞妞想了想,对小Q说:“对于这个数列,我每次询问你一个区间,你在1秒内把这个区间里的数字的和告诉我,如果你答得上来我就跟你一起去吃饭。”
由于妞妞最近沉迷王者荣耀,已经很久都没理过小Q了,所以小Q不想失去这次珍贵的机会,你能帮帮他吗?
输入格式
第一行,一个整数 q,表示妞妞的询问次数。
接下来 q 行,每行两个整数 l 和 r,表示妞妞询问的区间的左端点和右端点。
输出格式
共 q 行,每行一个整数,表示妞妞询问的区间和。
数据范围
1≤q≤105,
1≤l≤r≤109
输入样例1:
4
2 4
2 2
3 3
1 5
输出样例1:
3
2
-3
-3
输入样例2:
1
1 1000000000
输出样例2:
500000000
*思路:
如图所示:当我把整个序列俩俩配成一对,就可以出现1,2俩种情况。第2种情况很好理解,假设答案是res, 这个序列被分成K对,留下最后一个数R, 当是L是奇数时 ,每对的和是1,所以res = res + K + R, 当L是偶数时,每对的和是-1,res = res + (-K)+ R ;第一种情况不用加最后一个数,其他部分和第一种情况相同。
*代码
#include <iostream>
using namespace std;
int main() {
int q;
cin >> q;
while(q -- ) {
int res = 0, l, r;
cin >> l >> r;
int k = (r - l + 1) >> 1;
if(l & 1) res += k;
else res += (-k);
if((r - l + 1) & 1) {
if(r & 1) res += (-r);
else res += r;
}
cout << res << endl;
}
return 0;
}
腾讯暑假实习提前批C题 ******* 线性筛求阶乘
小Q和牛妹参加一个剪刀石头布的游戏,游戏用卡片来玩,每张卡片是剪刀,石头,布中的一种,每种类型的卡片有无限个。
牛妹从中选了n张卡片排成一排,正面朝下,小Q也会选择n张卡片排成一排,然后小Q和牛妹的卡片会依次进行比对,第一张对第一张,第二张对第二张…
如果小Q赢,小Q会得到一分,现在已知牛妹的每一张牌以及小Q最终的得分 s,请问小Q有多少种选择卡片的方案(多少不同的排列)
输入格式
第一行包含两个整数 n 和 s。
第二行包含 n 个整数,表示牛妹的每张卡片,每个数在[0,2]之间,0代表石头,1代表布,2代表剪刀。
输出格式
输出一个整数,表示总方案数对109+7取模后的值。
数据范围
1≤n≤2000,
0≤s≤2000
输入样例:
3 2
0 1 2
输出样例:
6
*思路:![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/021270e457fdc8822c53090da98e7ce1.png)
如图所以当输入n, s时,答案我们只要在n个位置选s个位置的动作赢然后乘上(n - s)个位置选平和输的动作就不会加分。所以答案就是res = C(n, s) * (2^(n-s)) , 难点在于怎么求组合数,因为n有2000多,我们可以线性筛c(n, s)=(n!)/((s!)*(n-s)!) ,求出分子和分母所有质因素的数目,然后相加减,就可以把除法换成乘法。不会超时咯。
#include <iostream>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 2010;
typedef long long LL;
int primes[N], cnt, a[N], powers[N];
bool st[N];
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; j < cnt && i * primes[j] <= n; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int get(int n, int p) {
int s = 0;
while(n > 0) {
s += n / p;
n /= p;
}
return s;
}
int main() {
int n, s;
cin >> n >> s;
for(int i = 0; i < n; i++) cin >> a[i];
if(s > n) {
cout << "0" << endl;
}
else {
get_primes(n);
for(int i = 0; i < cnt; i++) {
int p = primes[i];
powers[i] = get(n, p) - get(s, p) - get(n - s, p);
}
int res = 1;
for(int i = 0; i < cnt; i++) {
int p = primes[i];
while(powers[i] -- ) res = (1LL) * res * p % MOD;
}
for(int i = 0; i < n - s; i++) res = res * 2 % MOD;
cout << res << endl;
}
return 0;
}
腾讯暑假实习提前批D题 ********** 滑动窗口(双指针法)
小Q在进行射击气球的游戏,如果小Q在连续T枪中打爆了所有颜色的气球,将得到一只QQ公仔作为奖励。(每种颜色的球至少被打爆一只)。
这个游戏中有m种不同颜色的气球,编号1到m。
小Q一共有n发子弹,然后连续开了n枪。
小Q想知道在这n枪中,打爆所有颜色的气球最少用了连续几枪?
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,分别表示每一枪打中的气球的颜色,0表示没打中任何颜色的气球。
输出格式
一个整数表示小Q打爆所有颜色气球用的最少枪数。
如果小Q无法在这n枪打爆所有颜色的气球,则输出-1。
数据范围
1≤n≤106,
1≤m≤2000
输入样例:
12 5
2 5 3 1 3 2 4 1 0 5 4 3
输出样例:
6
样例解释
有五种颜色的气球,编号1到5。
游客从第二枪开始直到第七枪,这连续六枪打爆了5 3 1 3 2 4这几种颜色的气球,包含了从1到5的所有颜色,所以最少枪数为6。
*思路:
如图:用俩个指针维护L,R维护M种气球的区间, 求最小区间值。刚开始R++(红色箭头),当不是0号气球时并且i号气球第一次出现M++,一直到区间有M种气球时,判断当前气球数在区间有多个时或者为0号气球时,L++(绿色箭头)。更新最小区间。
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1000010;
int c[N], s[N];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
int colors = 0, res = n + 1;
for(int R = 1, L = 0 ; R <= n; R++) {
if(c[R] && !s[c[R]]) colors++;
s[c[R]] ++;
if(colors == m) {
while(s[c[L]] > 1 || !c[L]) {
s[c[L]] --;
L++;
}
res = min(res, R - L + 1);
}
}
if(res == n + 1) printf("-1\n");
else printf("%d\n", res);
return 0;
}