牛客算法周周练8
A,小A买彩票
题意
小A最近开始沉迷买彩票,并且希望能够通过买彩票发家致富。已知购买一张彩票需要3元,而彩票中奖的金额分别为1,2,3,4元,并且比较独特的是这个彩票中奖的各种金额都是等可能的。现在小A连续购买了n张彩票,他希望你能够告诉他至少能够不亏本的概率是多少。 0≤n≤30输入描述
一行一个整数N,为小A购买的彩票数量
输出描述
输出一个最简分数a/b,表示小A不亏本的概率。
若概率为1,则输出1/1,概率为0,则输出0/1。
解析
首先他要能不亏本,那么买一张的时候要买到3或者4,两张的时候要总和超过6,可以是33,34,43,44,24,42,这么多种情况,所以我们就不仅仅是考虑每一张都超过3,如果出现了一张1,来两张4就好了,所以我们考虑使用dp,来递推最多的可能性,之前都没有写过博客来讲一下dp,主要也是梳理一下自己对dp的认识关于dp
我对dp的认识主要是像背包问题这种,这个物体放进去还是不放.就像我现在背包已经装满了,装了i个物体,总质量为j,现在我要考虑是否要装第i+1个物体,如果没装满并且能装下,自然是直接装进去更好,那么如果装满了呢
我们用dp数组保存了每一种情况的最优解,也就是说假设我现在有2个物体a,b,a质量为1,价格为1,b质量为2,价格为2
我们先不管背包能装多少,首先可以得到dp[0][0]=0,也就是背包为空的时候,现在我们往后推我们dp数组为dp[1][1]也就是说之装一个物品,质量为1的时候显然只装一个a是最优解,然后我们递推,dp[1][2],也就是我们现在可以装的质量为2,还是只能装一个物体,如果我们不装物品b,那么dp[1][2]=dp[1][1]等于1,如果我们装物品2,那么我们就要在背包里腾出空间,拿出一个物品,然后装下物品2,dp[1][2]=dp[0][0]+2,这里就可以看到明显腾出空间再放入b更优,这样子一直往后推,当推到质量为我们所求的最大值的时候,得到的就一定是我们要的最优解。
好,梳理完了,我们来看现在这一题,我现在买了n张彩票,我现在要不亏本,那么我们最少就要中奖的总额达到3n,那么我们只要像前面一样,dp过去,然后在最后得到dp数组之和,将dp[n][3n]到dp[n][4*n]全部加起来就好了。
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll dp[32][150];
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(void){
ios::sync_with_stdio(false);
memset(dp,0,sizeof(dp));
int n;
cin>>n;
dp[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=1;j<=n*4;++j){
if(j>=1)
dp[i][j]+=dp[i-1][j-1];
if(j>=2)
dp[i][j]+=dp[i-1][j-2];
if(j>=3)
dp[i][j]+=dp[i-1][j-3];
if(j>=4)
dp[i][j]+=dp[i-1][j-4];
}
}
ll a=0;
for(int i=3*n;i<=121;++i){ //3*n为成本
a+=dp[n][i];
}
ll b=pow(4,n);
ll c=a/gcd(a,b);
ll d=b/gcd(a,b);
cout<<c<<"/"<<d<<endl;
return 0;
}
B,[金]点石成金
题意
赛时提示:魔法值和财富值初始为0
帕秋莉掌握了一种金属性魔法
她决定去捡一些石头,施展点石成金魔法
帕秋莉将捡到的n块石头排成一排,并决定将一些石头点为黄金
对于第i块石头,如果将其变为黄金,会增加ai的财富,消耗bi的魔法(需要说明的是,就算魔法值不够,也可以操作,操作后魔法值归零)
否则,帕秋莉将会回复ci的魔法,但减少di的财富(财富值同理,可以无限制减少)
帕秋莉想知道,按照1-n的顺序以此操作每块石头,如何决策,可以使自己最后的收益值最大
只需要输出最大收益
收益值=财富值*魔法值
(提示:数值不会变为负数,即任何时候,如果数值小于了0,它会立即变为0)
输入描述
第一行一个整数n
接下来n行,每行四个数,分别代表对应石头的a,b,c,d值
输出描述
一个整数表示答案
这个题目我都不想写。。。。。。直接暴力dfs,处理一点细节就可以直接冲!!!
上图水文章
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
typedef long long ll;
ll a[N], b[N], c[N], d[N];
int n;
ll dfs(int number, ll money, ll mofa){
if(number == n + 1){
return money * mofa;
}
ll temp = mofa - b[number];
ll res = dfs(number + 1, money + a[number], temp < 0 ? 0 : temp);
temp = money - d[number];
res = max(res, dfs(number + 1, temp < 0 ? 0 : temp, mofa + c[number]));
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d %d %d %d", &a[i], &b[i], &c[i], &d[i]);
}
ll res = dfs(1, 0, 0);
printf("%lld\n", res);
return 0;
}C.小阳的贝壳
题意
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 $col_i$。现在小阳有 3 种操作:1 l r x:给 [l,r][l,r] 区间里所有贝壳的颜色值加上 xx 。
2 l r:询问 [l,r][l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l = rl=r 输出 0)。
3 l r :询问 [l,r][l,r] 区间里所有贝壳颜色值的最大公约数。
输入描述
第一行输入两个正整数 n,mn,m,分别表示贝壳个数和操作个数。
第二行输入 n个数,表示每个贝壳的初始颜色。
第三到第 m + 2 行,每行第一个数为 opt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述
共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。
解析
线段树,俺不会隔壁大佬的代码大佬的博客代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
struct node{
int w,g,s;
}t[N<<2];
int a[N];
inline int my_max(int x,int y){//绝对值最值
return abs(x)>abs(y)?x:y;
}
inline void build(int now,int l,int r){
if(l==r){
t[now].w=t[now].g=t[now].s=(a[l]);
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid),build(now<<1|1,mid+1,r);
t[now].w=my_max(t[now<<1].w,t[now<<1|1].w);
t[now].g=__gcd(t[now<<1].g,t[now<<1|1].g);
t[now].s=t[now<<1].s+t[now<<1|1].s;
}
inline void insert(int now,int l,int r,int x,int y){
if(l==r){
t[now].w=t[now].g=t[now].s=(t[now].w+y);
return;
}
int mid=(l+r)>>1;
if(x<=mid){
insert(now<<1,l,mid,x,y);
}else{
insert(now<<1|1,mid+1,r,x,y);
}
t[now].w=my_max(t[now<<1].w,t[now<<1|1].w);
t[now].g=__gcd(t[now<<1].g,t[now<<1|1].g);
t[now].s=t[now<<1].s+t[now<<1|1].s;
}
inline int find1(int now,int l,int r,int lc,int rc){//区间绝对值最值
if(lc<=l&&r<=rc){
return t[now].w;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=my_max(res,find1(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=my_max(res,find1(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
inline int find2(int now,int l,int r,int lc,int rc){//区间gcd
if(lc<=l&&r<=rc){
return t[now].g;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=__gcd(res,find2(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=__gcd(res,find2(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
inline int find3(int now,int l,int r,int lc,int rc){//区间和
if(lc<=l&&r<=rc){
return t[now].s;
}
int mid=(l+r)>>1,res=0;
if(lc<=mid){
res=(res+find3(now<<1,l,mid,lc,rc));
}
if(rc>mid){
res=(res+find3(now<<1|1,mid+1,r,lc,rc));
}
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for(int i=n;i;--i){
a[i]-=a[i-1];
}
build(1,1,n);
while(m--){
int opt,l,r;
scanf("%d%d%d",&opt,&l,&r);
if(opt==1){
int x;
scanf("%d",&x);
insert(1,1,n,l,x);
if(r<n){
insert(1,1,n,r+1,-x);
}
}else if(opt==2){
if(l==r){
puts("0");
continue;
}
printf("%d\n",abs(find1(1,1,n,l+1,r)));
}else{
if(l==r){
printf("%d\n",find3(1,1,n,1,l));
}else{
printf("%d\n",abs(__gcd(find3(1,1,n,1,l),find2(1,1,n,l+1,r))));
}
}
}
return 0;
}D.Forsaken喜欢字符串
待补E.Board
题意
恬恬有一个nx n的数组。她在用这个数组玩游戏:
开始时,数组中每一个元素都是0。
恬恬会做某些操作。在一次操作中,她可以将某一行的所有元素同时加上一个值,也可以将某一列的所有元素同时加上一个值。
在几次操作后,一个元素被隐藏了。你能帮助她回忆隐藏的数是几吗?
输入描述
第一行一个整数n(1≤ n≤ 1000)。
接下来n行每行n个整数表示数组a。
第(i+1)行的第j个元素表示aij(aij=-1或0≤ aij ≤ 10000)。-1表示隐藏的元素。
输出描述
仅一个整数表示答案。
解析
这个题目就是每次我可以个一行或者一列都加上一,然后我现在隐藏了一个点的值,让你根据别的点的值来求出这个点,主要是考察思维,我上图理解一下就好了
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=1005;
int s[MAXN][MAXN];
int main(void){
ios::sync_with_stdio(false);
int n;
cin>>n;
int a,b;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
cin>>s[i][j];
if(s[i][j]==-1)
a=i,b=j;
}
}
int ans=0;
if(a==0||b==0)cout<<s[a][n-1]+s[n-1][b]-s[n-1][n-1];
else cout<<s[a][0]+s[0][b]-s[0][0];
return 0;
}完结撒花,又写(水)完了一篇长博客
基恩士成长空间 419人发布

