题解

消消看

知识点:贪心,排序
题意:给定一个01串,每次可以选择一个连续的0或者1子串消除。两个人轮流消除,求双方都最优选择时,胜者是谁以及分数之差。
思路:显然不可能选择'0'子串。并且有多个'1'子串时会选择最长的那个1串。因此用贪心策略即可,预处理出所有'1'子串的长度,然后从大到小选择即可。
复杂度

#include<bits/stdc++.h>
using namespace std;
int main(){

    int t;
    cin>>t;
    while(t--){
        vector<int>v;
        int n,i,cnt=0;
        string s;
        cin>>s;
        for(i=0;i<s.length();i++){
            if(s[i]=='1')cnt++;
            else v.push_back(cnt),cnt=0;
        }
        v.push_back(cnt);
        sort(v.begin(),v.end());
        int c1=0,c2=0;
        for(i=0;i<v.size();i++){
            if(i%2)c2+=v[i];
            else c1+=v[i];
        }
        if(c1<c2)cout<<"Niumei\n"<<c2-c1<<endl;
        else if(c1==c2)cout<<"Draw\n";
        else cout<<"Niumei\n"<<c1-c2<<endl;
    }
}

赏金猎人

知识点:贪心,排序,优先队列
题意:每个人有一个战斗力和金钱。每个人只能击杀比自己战斗力低的人,并获得他的金钱。问在最多击杀 个人的前提下,每个人最多的钱是多少?
思路:对于每个人而言,只需要在战斗力比他小的人里,找出 个最大的金钱即可。观察到 的范围不超过10,因此可以用优先队列(最大堆)解决这个问题。
写代码的时候要注意细节,处理“战斗力相等”的人的时候要格外小心。
复杂度

using namespace std;
struct people{
    int attack,money,id,res;
};

people a[100010];
bool cmp(people a,people b){
    return a.attack<b.attack;
}
bool cmp_by_id(people a,people b){
    return a.id<b.id;
}
priority_queue<int>q;
int main(){
    int n,k,i,j;
    cin>>n>>k;
    for(i=0;i<n;i++)cin>>a[i].attack;
    for(i=0;i<n;i++){
        cin>>a[i].money;
        a[i].id=i;
        a[i].res=a[i].money;
    }
    sort(a,a+n,cmp);
    vector<int>temp;
    for(i=0;i<n;i++){
        vector<int>to_be_killed;
        int now=q.size();
        for(j=0;j<min(k,now);j++){
            a[i].res+=q.top();
            to_be_killed.push_back(q.top());
            q.pop();
        }
        for(j=0;j<to_be_killed.size();j++){
            q.push(to_be_killed[j]);
        }
        temp.push_back(a[i].money);
        to_be_killed.clear();
        if(i<n-1&&a[i].attack<a[i+1].attack){
            for(j=0;j<temp.size();j++){
                q.push(temp[j]);
            }
            temp.clear();
        }
    }
    sort(a,a+n,cmp_by_id);
    for(i=0;i<n;i++){
        cout<<a[i].res<<" ";
    }

}

身份证

解法一

知识点:排序、二分
思路:可以发现每个数都在 的数据范围内。对于一个十八位数 而言, 和它前 个数字相等意味着除了前 个数字以外, 的后 个数字一定在 000..00到 999..99范围内。
例如,对于数字123451234512345123,如果另一个数和它有长度为5的公共前缀,那么这个数的取值范围是 123450000000000000到123459999999999999。
所以这道题可以用二分来解,对于每个数,二分去找其前缀长度为 的合法上界和下界,若该上界和下界中包含的数不为零,那么意味着存在该公共前缀的数。
复杂度

解法二

知识点:字典树
思路:如果熟悉字典树的话可以发现这是一道字典树的裸题。建一棵字典树,每个节点上存该前缀的字符串数量。然后就可以做到每次 进行查询了。
复杂度
参考代码(二分解法):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
vector<long long>v;
ll pow10[20];
int main(){
    int n,q,i;
    pow10[0]=1;
    for(i=1;i<18;i++)pow10[i]=pow10[i-1]*10;
    cin>>n>>q;
    for(i=0;i<n;i++){
        ll x;
        cin>>x;
        v.push_back(x);
    }
    sort(v.begin(),v.end());
    while(q--){
        ll x;
        cin>>x;
        ll j=17;
        ll out1=0,out2=0;
        for(i=1;i<=18;i++,j--){
            ll l=x/pow10[j]*pow10[j],r=(x/pow10[j]+1)*pow10[j];
            ll c1=lower_bound(v.begin(),v.end(),l)-v.begin();
            ll c2=upper_bound(v.begin(),v.end(),r)-v.begin();
            if(c1==c2)break;
            out1=i,out2=c2-c1;
        }
        if(out1==0)out2=n;
        cout<<out1<<" "<<out2<<endl;
    }
}
全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务