笔试强训:Week-6

目录

一、小红的口罩(贪心+优先级队列)

二**、春游(分类讨论+数学)

三**、数位染色(01背包)

四、素数回文(模拟+数学)

五、活动安排(贪心+自定义排序/动规)

六**、合唱团(多状态dp)

七、跳台阶扩展问题(规律)

八、包含不超过两种字符的最长子串(滑动窗口)

九、字符串的排列(排序+dfs全排列带去重)

十、ISBN号码(模拟)

十一、kotori和迷宫(单源最短路bfs)

十二、矩阵最长递增路径(单源bfs / dfs+记忆化)

十三**、奇数位丢弃(找规律)

十四**、求和(dfs)

十五**、计算字符串的编辑距离(dp)

十六、提取不重复的整数(模拟)

十七、哈夫曼编码(模板)

十八**、abb(dp+哈希)


一、小红的口罩(贪心+优先级队列)

小红的口罩

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int n,k;
int main(){
    //用一个小根堆  每次使用不舒适度最小的
    cin>>n>>k;
    int x;
    priority_queue<int,vector<int>,greater<int>> heap;
    for(int i=0;i<n;++i){
        cin>>x;
        heap.push(x);
    }
    //开始循环
    int sum=0,ret=0;
    while(sum<=k){
        int t=heap.top();
        heap.pop();
        sum+=t;
        if(sum>k)break;
        heap.push(t*2);
        ++ret;
    }
    cout<<ret<<endl;
}

二**、春游(分类讨论+数学)

春游

先考虑恰好可以每条船坐满,那就是看双人船和三人船每个人A多少钱,哪个小就选哪个。如果人数取模于这个更少A的船的人数刚好等于0,那最好了,那就是我们要的方案。

如果有余,就考虑最后余下的那个人,和前面几个人组合。

如果是双人船的方案,最后要么剩下那个人坐双,要么坐单,要么和前面两个人改坐三人。

如果是三人船的方案,最后要么剩下一个要么剩下两个,这个人要么一个人坐便宜的那个,要么和前面三个加上他自己一个组合,双双,要么双三肯定是双三。或者最后剩下两个人,要么这两人一起坐便宜的那个,要么和前面三个人凑三个双,要么就是一双一三,要么就是两三,一双一三和两三不就是前面那个人单独坐的情况。

所以最终就得到上图绿色的,下面开始编写代码

#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
LL t,n,a,b;
LL func(){
    if(n<=2) return min(a,b);//做便宜的船
    //ceil向上取整
    if(a>=b) return (LL)ceil(n/3.0)*b;
    LL ret=0;
    if(a*3<b*2){//说明双人船便宜 尽量选择双人船
        ret+=n/2*a;
        if(n%2==1) ret+=min(a,b-a);//多1个人 看看是要直接租双人 还是退掉一个双人租三人
    }
    else{//说明要尽可能租三人船  b*2<=a*3 有可能b更便宜
        ret+=n/3*b;
        //此时可能剩1个人或者2个人
        n%=3;
        if(n==1) ret+=min(a,2*a-b);
        if(n==2) ret+=a;
    }
    return ret;
}
int main(){
    cin>>t;
    while(t--){
        cin>>n>>a>>b;
        cout<<func()<<endl;
    }
}

三**、数位染色(01背包)

数位染色_牛客题霸_牛客网

#include <iostream>
#include<vector>
using namespace std;

int main() {
    long long x,tmp,sum;
    cin>>x;
    tmp=x;
    sum=0;
    vector<int> arr;
    while(tmp)
    {
        arr.push_back(tmp%10);
        sum+=tmp%10;
        tmp/=10;
    }
    if(sum%2)cout<<"No";
    else{
        long long target=sum/2;
        int n=arr.size();
        vector<vector<bool>> dp(n+1,vector<bool>(target+1));
        dp[0][0]=true;
        for(int i=1;i<=n;++i)
        {
            for(int j=arr[i-1];j<=target;++j)
            {
                dp[i][j]=dp[i-1][j]||dp[i-1][j-arr[i-1]];
            }
        }
        if(dp[n][target])cout<<"Yes";
        else cout<<"No";
    }
    return 0;
}

进一步空间优化,注意要倒着填,否则顺着填当计算dp[j]时,dp[j-arr[i]]可能已经被当前轮次更新过了

#include <iostream>
using namespace std;
const int N=20,M=10*9;//记录所有数位
bool dp[M];//前i个数选 和恰好为target
int arr[N];//标记各个数位
long long x;
int n=0;//统计有多少位
int sum=0;//统计总和
bool func(){
    if(sum%2==1) return false;
    sum/=2;
    dp[0]=true;
    for(int i=0;i<n;++i)
      for(int j=sum;j>=arr[i];--j)
        dp[j]=dp[j]||dp[j-arr[i]];
    return dp[sum]; 
}
int main() {
    cin>>x;
    while(x){
       arr[n++]=x%10;
       sum+=x%10;
       x/=10;
    }
    if(func()) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}

四、素数回文(模拟+数学)

素数回文_牛客题霸_牛客网

素数判断前面我们已经掌握了,所以这题就是创建回文数的模拟题

#include <cmath>
#include <iostream>
using namespace std;
bool IsPrime(long long x)
{
    if(x%2==0)return false;
    for(int i=3;i<=sqrt(x);i+=2)
    {
        if(x%i==0)return false;
    }
    return true;
}
int main() {
    string t;
    cin>>t;
    int cur=t.length()-2;
    while(cur>=0)
    {
        t+=t[cur];
        --cur;
    }
    long long x=stoll(t);
    // cur=0;
    // while(cur<t.length()){
    //     x=x*10+t[cur]-'0';
    //     ++cur;
    // }

    if(IsPrime(x))cout<<"prime";
    else cout<<"noprime";

    return 0;
}

五、活动安排(贪心+自定义排序/动规)

活动安排_牛客题霸_牛客网

0位置相等的按1位置降序排,0位置不等的按0位置升序排,dp填表的时候往前找,如果0位置相等跳过,不等的时候就找有没有1位置比我的0位置小的。如果有直接等于该位置长度+1,贪心的break直接将效率最大化

#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
struct cmp{
    bool operator()(const vector<int> &a1,const vector<int> &a2){
        if(a1[0]==a2[0]){
            return a1[1]>a2[1];
        }
        return a1[0]<a2[0];
    }
};
int main() {
    int n;
    cin>>n;
    vector<vector<int>> arr(n,vector<int>(2));
    for(int i=0;i<n;++i)cin>>arr[i][0]>>arr[i][1];
    sort(arr.begin(),arr.end(),cmp());
    // for(int i=0;i<n;++i)cout<<arr[i][0]<<" "<<arr[i][1]<<endl;
    vector<int> dp(n+1,1);
    for(int i=1;i<=n;++i)
    {
        for(int j=i-1;j>=1;--j)
        {
            if(arr[i-1][0]==arr[j-1][0])continue;
            if(arr[i-1][0]>=arr[j-1][1])
            {
                dp[i]=dp[j]+1;
                break;
            }
        }
    }
    cout<<dp[n];

    return 0;
}

或者不用动规,摘自别人的题解

#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=2e5+1;
int n;
PII arr[N];
int main() {
   cin>>n;
   for(int i=0;i<n;++i) cin>>arr[i].first>>arr[i].second;
   sort(arr,arr+n);
   int ret=0,right=arr[0].second;
   for(int i=1;i<n;++i) 
   //如果有重叠 保留右侧最小的
      if(arr[i].first<right) right=min(right,arr[i].second);
      else{//如果没有重叠 就更新一下 然后++ret
         ++ret;
         right=arr[i].second;
      }
    cout<<ret+1<<endl;
}

六**、合唱团(多状态dp)

合唱团_牛客题霸_牛客网

做的时候意识到是多状态dp,但是选择k个同时保证相连两个距离不超过d,不知道如何处理

对于人数我们可以再开一个维度表示选择了几个人,第一个维度代表从[1,i]中挑选的最大乘积,因为有d的限制,所以我们设置为arr[i]为必选,这样dp填表的时候只需要考虑i~max(j-1,i-d)的位置即可。

看完解析的细节问题,感觉这题配得上困难题

代码最终实现如下:

#include <iostream>
#include <vector>
using namespace std;
const long long N=0x3f3f3f3f3f3f3f3f;
int main() {
    int n,k,d;
    cin>>n;
    vector<int> arr(n+1);
    for(int i=1;i<=n;++i)cin>>arr[i];
    cin>>k>>d;

    //f存正,g存负
    vector<vector<long long>> f(n+1,vector<long long>(k+1,-N));
    vector<vector<long long>> g(n+1,vector<long long>(k+1,N));

    //填表
    for(int i=1;i<=n;++i)
    {
        //初始化
        f[i][1]=g[i][1]=arr[i];
        //从2开始,1我们已经填过了
        for(int j=2;j<=min(i,k);++j)
        {
            for(int prev=i-1;prev>=max(i-d,j-1);--prev)
            {
                if(arr[i]>0)
                {
                    f[i][j]=max(f[i][j],f[prev][j-1]*arr[i]);
                    g[i][j]=min(g[i][j],g[prev][j-1]*arr[i]);
                }
                else if(arr[i]<0)
                {
                    f[i][j]=max(f[i][j],g[prev][j-1]*arr[i]);
                    g[i][j]=min(g[i][j],f[prev][j-1]*arr[i]);
                }
                else f[i][j]=g[i][j]=0;
            }
        }
    }
    long long ans=-N;
    for(int i=k;i<=n;++i)
    {
        ans=max(ans,f[i][k]);
    }
    cout<<ans;
    return 0;
}

今天三题一遍过

七、跳台阶扩展问题(规律)

跳台阶扩展问题_牛客题霸_牛客网

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    int n;
    cin>>n;
    cout<<pow(2,n-1);
    return 0;
}

八、包含不超过两种字符的最长子串(滑动窗口)

包含不超过两种字符的最长子串_牛客题霸_牛客网

#include <iostream>
#include<unordered_map>
using namespace std;

int main() {
    string n;
    cin>>n;
    int len=0,left=0,right=0;
    unordered_map<char,int> hash;
    while(right<n.size())
    {
        char in=n[right];
        ++hash[in];
        while(hash.size()>2)
        {
            char out=n[left];
            if(--hash[out]==0)hash.erase(out);
            ++left;
        }
        len=max(len,right-left+1);
        ++right;
    }
    cout<<len;
    return 0;
}

九、字符串的排列(排序+dfs全排列带去重)

字符串的排列_牛客题霸_牛客网

牛客好像不能像力扣一样,成员变量当作全局变量自动初始化,得放在类外

#include <vector>
string path;
bool check[11];
vector<string>  ret;
class Solution {
public:
    void dfs(string str,int pos)
    {
        if(pos==str.size())
        {
            ret.push_back(path);
            return;
        }
        for(int i=0;i<str.size();++i)
        {
            if(!check[i])
            {
                if(i==0||str[i]!=str[i-1]||check[i-1])
                {
                    path.push_back(str[i]);
                    check[i]=true;
                    dfs(str,pos+1);
                    path.pop_back();
                    check[i]=false;
                }
            }
        }
    }
    vector<string> Permutation(string str) {
        if(str=="")return ret;

        sort(str.begin(),str.end());
        dfs(str,0);
        return ret;
    }
};

今天三题也拿下了

十、ISBN号码(模拟)

ISBN号码_牛客题霸_牛客网

#include <iostream>
using namespace std;

int main() {
    string s;
    cin>>s;
    int cur=0,num=1,sum=0;
    while(cur<11)
    {
        if(s[cur]=='-')
        {
            ++cur;
            continue;
        }
        int x=s[cur]-'0';
        sum+=(x*num);
        ++cur;
        ++num;
    }
    int right_num=sum%11;
    if(s[12]==right_num+'0'||(s[12]=='X'&&right_num==10))
    {
        cout<<"Right";
    }
    else{
        s.pop_back();
        if(right_num==10)s+='X';
        else{
            s+=(right_num+'0');
        }
        cout<<s;
    }
    return 0;
}

十一、kotori和迷宫(单源最短路bfs)

kotori和迷宫

#include <iostream>
#include <vector>
#include<queue>
using namespace std;
using PII=pair<int,int>;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
bool check[50][50];
int main() {
    int n,m;
    cin>>n>>m;
    queue<PII> q;
    vector<string> grid(n);
    for(int i=0;i<n;++i)
    {
        cin>>grid[i];
        for(int j=0;j<m;++j)
        {
            if(grid[i][j]=='k')
            {
                check[i][j]=true;
                q.push({i,j});
            }
        }
    }

    int steps=0,ways=0;
    while(!q.empty())
    {
        int sz=q.size();
        if(ways==0)
        {
            ++steps;
        }
        while(sz--)
        {
            auto [i,j]=q.front();
            q.pop();
            for(int k=0;k<4;++k)
            {
                int x=i+dx[k],y=j+dy[k];
                if(x>=0&&x<n&&y>=0&&y<m&&!check[x][y]&&grid[x][y]=='e')
                {
                    ++ways;
                    check[x][y]=true;
                    continue;
                }
                if(x>=0&&x<n&&y>=0&&y<m&&!check[x][y]&&grid[x][y]=='.')
                {
                    check[x][y]=true;
                    q.push({x,y});
                }
            }
        }
    }
    if(ways)cout<<ways<<" "<<steps;
    else cout<<-1;
    return 0;
}

十二、矩阵最长递增路径(单源bfs / dfs+记忆化)

矩阵最长递增路径_牛客题霸_牛客网

bfs解法

int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int _n,_m;
using PII=pair<int,int>;
class Solution {
public:
    int bfs(vector<vector<int> >& matrix,int i,int j)
    {
        int steps=0;
        queue<PII> q;
        q.push({i,j});
        while(!q.empty())
        {
            int sz=q.size();
            ++steps;
            while(sz--)
            {
                auto [i,j]=q.front();
                q.pop();
                for(int k=0;k<4;++k)
                {
                    int x=i+dx[k],y=j+dy[k];
                    if(x>=0&&x<_n&&y>=0&&y<_m&&matrix[x][y]>matrix[i][j])
                    {
                        q.push({x,y});
                    }
                }
            }
        }
        return steps;
    }
    int solve(vector<vector<int> >& matrix) {
        _n=matrix.size(),_m=matrix[0].size();
        int len=0;
        for(int i=0;i<_n;++i)
        {
            for(int j=0;j<_m;++j)
            {
                len=max(len,bfs(matrix,i,j));
            }
        }
        return len;
    }
};

dfs记忆化

#include <vector>
int memo[1001][1001],dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int _m,_n,ret;
class Solution {
public:
    int solve(vector<vector<int> >& matrix) {
        _m=matrix.size(),_n=matrix[0].size();
        for(int i=0;i<_m;++i)
        {
            for(int j=0;j<_n;++j)
            {
                ret=max(ret,dfs(matrix,i,j));
            }
        }
        return ret;
    }
    int dfs(vector<vector<int>>&matrix,int i,int j)
    {
        if(memo[i][j])return memo[i][j];
        int path=1;
        for(int k=0;k<4;++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x>=0&&x<_m&&y>=0&&y<_n&&matrix[x][y]>matrix[i][j])
            {
                path=max(path,dfs(matrix,x,y)+1);
            }
        }
        memo[i][j]=path;
        return path;
    }
};

事不过三,今天三题一题都没AC

十三**、奇数位丢弃(找规律)

奇数位丢弃_牛客题霸_牛客网

对于多组数据直接while(cin>>n)即可

#include <iostream>
using namespace std;
 
int main() {
   int n;
   while(cin>>n){
     int ret=1;
     while(ret-1<=n) ret*=2;
     cout<<ret/2-1<<endl;
   }
    return 0;
}

十四**、求和(dfs)

求和__牛客网

dfs每层为选择几个,不过已经忘了代码怎么写的了,或者选和不选也可以做,哎呀,看来是大脑偷懒了

选还是不选为决策

#include <iostream>
using namespace std;
bool vis[11];//标记数组
int n,m;
int sum;//统计已选数字的总和
void dfs(int pos){
    if(sum==m){
       for(int i=1;i<=n;++i)
         if(vis[i]) cout<<i<<" ";
       cout<<endl;
       return;
    }
    if(sum>m||pos>n) return;//没有意义了
    //选
    sum+=pos;
    vis[pos]=true;
    dfs(pos+1);
    sum-=pos;
    vis[pos]=false;
    dfs(pos+1);//不选
}   
int main() {
   cin>>n>>m;
   dfs(1);//dfs枚举位置
}

每层选几个为决策,其实就是求所有子序列

#include <iostream>
using namespace std;
bool vis[11];//标记数组
int n,m;
int sum;//统计已选数字的总和
void dfs(int pos){
    if(sum==m){
       for(int i=1;i<=n;++i)
         if(vis[i]) cout<<i<<" ";
       cout<<endl;
       return;
    }
    if(sum>m) return;//没有意义了
    for(int i=pos;i<=n;++i){
        sum+=i;
        vis[i]=true;
        dfs(i+1);
        vis[i]=false;
        sum-=i;
    }
}   
int main() {
   cin>>n>>m;
   dfs(1);//dfs枚举位置
}

十五**、计算字符串的编辑距离(dp)

计算字符串的编辑距离_牛客题霸_牛客网

dp神力

#include <iostream>
#include <string>
using namespace std;
const int N=1010;
int dp[N][N];
string a,b;
int main() {
   //dp[i][j]表示以0-i的A串 变成0-j的B串所需要的最小操作次数
   cin>>a>>b;
   int m=a.size(),n=b.size();
   a=' '+a,b=' '+b;//为了同步下标
   dp[0][0]=0;//都是空串
   for(int i=1;i<=m;++i) dp[i][0]=i;
   for(int j=1;j<=n;++j) dp[0][j]=j;
   for(int i=1;i<=m;++i)
     for(int j=1;j<=n;++j)
       if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1];
       else dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
    cout<<dp[m][n]<<endl;
}

十六、提取不重复的整数(模拟)

提取不重复的整数_牛客题霸_牛客网

#include <iostream>
#include<unordered_map>
using namespace std;

int main() {
    unordered_map<char,int> hash;
    string s;
    cin>>s;
    int cur=s.length()-1;
    string ans;
    while(cur>=0)
    {
        while(cur>=0&&hash.count(s[cur]))--cur;
        if(cur<0)break;

        ++hash[s[cur]];
        ans.push_back(s[cur]);
        --cur;
    }
    cout<<ans;
    return 0;
}

十七、哈夫曼编码(模板)

哈夫曼编码_牛客题霸_牛客网

#include <iostream>
#include <queue>
using ll=long long;
using namespace std;

int main() {
    ll n,x;
    cin>>n;
    priority_queue<ll,vector<ll>,greater<ll>> hp;
    while(n--)
    {
        cin>>x;
        hp.push(x);
    }
    ll sum=0;
    while(hp.size()>=2)
    {
        ll a=hp.top();
        hp.pop();
        ll b=hp.top();
        hp.pop();
        ll num=a+b;
        sum+=num;
        hp.push(num);
    }
    cout<<sum;
    return 0;
}

十八**、abb(dp+哈希)

abb_牛客题霸_牛客网

感觉难度上了一个level

#include <iostream>
using namespace std;
const int N=1e5+10;
typedef long long LL;
char s[N];
LL f[26],g[26];
//f[]表示以i位置为结尾时有多少个_x
//g[]表示以i位置为结尾时有多少个x
int n;
int main() {
    cin>>n>>s;
    LL ret=0;
    for(int i=0;i<n;++i){
        int x=s[i]-'a';
        ret+=f[x];
        f[x]+=i-g[x];
        g[x]+=1;
    }
    cout<<ret<<endl;
}

本周完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_dindong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值