牛客练习赛23 :A B C D

A 贪心思想,尽量选大的,这样支出的纸票数量和硬币数量之和最小
代码:

#include<bits/stdc++.h>
using namespace std;

int yuan[]={100,50,20,10,5,2,1};
int fen[]={50,20,10,5,2,1};
int num[20];

int main(){
    int T;
    scanf("%d",&T);
    while(T--){

        int a,b;
        scanf("%d%d",&a,&b);
        for(int i=0;i<7;i++){

            num[i]=a/yuan[i];
            a-=num[i]*yuan[i];
        } 
        for(int i=0;i<6;i++){

            num[i+7]=b/fen[i];
            b-=num[i+7]*fen[i];
        } 
        for(int i=0;i<=12;num[i]=0,i++) printf("%d%c",num[i],i==12?'\n':' ');
    } 
} 

B 找规律
设 f[i] 代表数字 i 的最大得分
我们可以得到:
f[1]=0
f[2]=1*1=1
f[3]=f[2]+f[1]+2*1=1+0+2=3
f[4]=f[2]+f[2]+2*2=1+1+4=6
f[5]=f[3]+f[2]+3*2=3+1+6=10
f[6]=f[3]+f[3]+3*3=3+3+9=15
f[7]=f[4]+f[3]+4*3=6+3+12=21

为什么我们要把 n 分成均匀或者接近均匀的两份呢?因为基于贪心思想,这样会使得结果更大啊,证明?不会诶。。。
接下来这个规律就好找了吧
f[2]-f[1]=1
f[3]-f[2]=2
f[4]-f[3]=3
f[5]-f[4]=4
f[6]-f[5]=5
f[7]-f[6]=6

f[n]-f[n-1]=n-1

所以f[n]=(1+n-1)(n-1)/2=n(n-1)/2
其实就是公差为1的等差数列前缀和

代码:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){

       int n;
       scanf("%d",&n);
       printf("%lld\n",(long long)n*(n-1)/2);
    } 
} 

C 其实多想一想,就可以做出来
K 没看到要输出,白白WA了四次,以为是自己代码有问题,就检查代码去了,输出都看不清楚。。。

我的思路:
从大到小枚举按位与的最低位bit(0<=bit<=30),也就是枚举30-0这些位
如果符合情况就立即break,也就是找到答案了,并且要实时保存集合里面符合情况的数
不符合就继续
怎么知道符不符合情况呢?其实你想一想,当一个数第bit位为1时,我们肯定是要把它加入集合的,为什么?我们加入这个数的话,比bit位低的位就更有可能按位与为0了啊,这样按位与的最低位就更有可能为bit位 ,是不是?想一想
所以枚举按位与的最低位bit时,我们就把第bit位为1的数全部按位与起来,如果得到的数的最低位小于1 << bit,就不符合情况,因为存在比bit位更低位全部为1的情况,这样bit位就不是按位与的最低位了。

要是还没理解,看代码吧
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=100000+100;

int ans[maxn]; 
int tmp[maxn];

int main(){

    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&ans[i]);
    int tot=0;
    for(int bi=30;bi>=0;bi--){

        int now=-1;
        for(int i=0;i<n;i++){

           if((ans[i]>>bi)&1){

               tmp[++tot]=ans[i];
               if(now==-1) now=ans[i];
               else now=now&ans[i];
           } 
        }
        if(now==-1 || (now&(-now))<(1<<bi)) tot=0;
        else break;
    } 
    if(tot==0){

        printf("-1\n");
        return 0;
    }
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++)  printf("%d%c",tmp[i],i==tot?'\n':' ');
} 

D 开始题意理解错了?原来每种排列只能贡献一次,这样不就简单了
我们可以枚举所有 ‘a’-‘i’ 的排列,看这种排列是否是字符串的子序列,是的话贡献就+1
怎么判断该排列是否是字符串的子序列呢?
每一种排列的每个字符都有顺序的吧(也就是位置关系),前一个字符比后一个字符出现的位置肯定是要靠前的,也就是下标要小一点,所以我们只需要判断该排列的一个字符在字符串中出现的位置是否都存在大于前面字符在字符串中出现的位置,我提前用vector存好了 ’a’-‘i’ 在字符串的多个位置,所以判断位置关系,在这些vector上进行二分即可(详细看代码),如果排列的每个位置字符都满足情况,就说明该排列是字符串的子序列,贡献+1
详细请看代码吧

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int maxn=3000+100;

char ch[maxn];
int id[maxn];
vector<int>G[10];
int ind[10]={0,1,2,3,4,5,6,7,8}; //相当于'a'-'i'

int main(){

    while(scanf("%s",ch)==1){

        int len=strlen(ch);
        for (int i=0;i<len;i++){

            id[i]=ch[i]-'a';
            G[id[i]].push_back(i);
        }
        int ans=0;
        do{
            int locate=-1;
            bool isok=true;
            for (int i=0;i<9;i++) {

                locate=lower_bound(G[ind[i]].begin(),G[ind[i]].end(),locate)-G[ind[i]].begin();
                if (locate>=G[ind[i]].size()) {

                    isok=false;
                    break;
                }
                locate=G[ind[i]][locate];
            }
            if(isok) ans++;
        }while(next_permutation(ind,ind+9));
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37960603/article/details/81255729