笔试强训:Week -7

目录

一、旋转字符串(暴力/规律)

二、合并k个已排序的链表(归并/优先级队列+自定义排序)

三、滑雪(bfs)

四、天使果冻(递推)

五、dd爱旋转(模拟+利用规律优化)

六**、小红取数(01背包+同余定理)

七、神奇的字母II(哈希)

八、字符编码(哈夫曼编码)

九*、最少的完全平方数(完全背包)

最少完全平方数避免贪心策略

十**、游游的字母串(优化枚举)

十一、体育课测试II (BFS拓扑排序)

十二*、合唱队形(序列dp)

初始化问题

十三、棋子翻转(模拟)

十四**、宵暗的妖怪(区间dp)

十五**、过桥(贪心+BFS+双指针优化掉队列)

十六、最大差值

十七**、兑换零钱(完全背包)

十八**、小红的子串(字符种类在L到R之间的子串个数)(滑动窗口+前缀和)


一、旋转字符串(暴力/规律)

旋转字符串_牛客题霸_牛客网

暴力:

class Solution {
public:
    bool solve(string A, string B) {
        if(A.length()!=B.length())return false;

        int n=A.length();
        for(int i=0;i<A.size();++i)
        {
            if(A[i]==B[0])
            {
                int tail=(i-1+n)%n,curA=i,curB=0;
                while(curA!=tail)
                {
                    if(A[curA]!=B[curB])break;

                    curA=(curA+1)%n;
                    ++curB;
                }
                if(curA==tail)return true;
            }
        }
        return false;
    }
};

二、合并k个已排序的链表(归并/优先级队列+自定义排序)

合并k个已排序的链表_牛客题霸_牛客网

看到算法复杂度nlogn就该想到归并排序

归并排序:

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
#include <vector>
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n=lists.size();
        if(n==0)return nullptr;
        return My_merge(lists,0,n-1);
    }
    ListNode* My_merge(vector<ListNode*>&lists,int left,int right)
    {
        if(left==right)return lists[left];

        int mid=left+(right-left)/2;
        ListNode*l1=My_merge(lists,left,mid);
        ListNode*l2=My_merge(lists,mid+1,right);
        return MergeTwoList(l1,l2);
    }
    ListNode* MergeTwoList(ListNode*l1,ListNode*l2)
    {
        if(l1==nullptr)return l2;
        if(l2==nullptr)return l1;

        ListNode*cur1=l1,*cur2=l2,*newhead=new ListNode(-1),*tail=newhead;
        while(cur1&&cur2)
        {
            if(cur1->val<cur2->val)
            {
                tail->next=cur1;
                cur1=cur1->next;
            }
            else {
                tail->next=cur2;
                cur2=cur2->next;
            }
            tail=tail->next;
        }
        while(cur1)
        {
            tail->next=cur1;
            tail=tail->next;
            cur1=cur1->next;
        }
        while(cur2)
        {
            tail->next=cur2;
            tail=tail->next;
            cur2=cur2->next;
        }
        tail=newhead->next;
        delete newhead;
        return tail;
    }
};

优先级队列+自定义排序:

注意开始入堆的时候要判断是不是有空数组,否则段溢出

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
#include <queue>
class Solution {
public:
    struct cmp
    {
        bool operator()(const ListNode*l1,const ListNode*l2)
        {
            return l1->val > l2->val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.empty())return nullptr;

        priority_queue<ListNode*,vector<ListNode*>,cmp> hp;
        for(auto&l:lists)
        {
            //这里需要判断一下
            if(l)hp.push(l);
        }
        ListNode*newhead=new ListNode(-1),*tail=newhead;
        while(!hp.empty())
        {
            ListNode* hp_top=hp.top();
            hp.pop();
            tail->next=hp_top;
            tail=tail->next;
            if(hp_top->next)hp.push(hp_top->next);
        }
        tail->next=nullptr;
        tail=newhead->next;
        delete newhead;
        return tail;
    }
};

三、滑雪(bfs)

滑雪_牛客题霸_牛客网

这里地图长宽都是小于100的,如果数据量来到1000,使用dfs会因为递归深度过大导致爆栈,报错显示为段溢出,因此地图题还是使用bfs为妙。

#include <iostream>
#include<queue>
using namespace std;
const int N=101;
using PII=pair<int,int>;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},n,m,grid[N][N];
int bfs(int i,int j)
{
    queue<PII> q;
    q.push({i,j});
    int step=0;
    while(!q.empty())
    {
        ++step;
        int sz=q.size();
        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&&grid[x][y]>grid[i][j])
                    q.push({x,y});
            }
        }
    }
    return step;
}
int main() {
    cin>>n>>m;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<m;++j)cin>>grid[i][j];
    }

    int len=1;
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<m;++j)len=max(len,bfs(i,j));
    }
    cout<<len;
    
    return 0;
}

四、天使果冻(递推)

天使果冻

#include <climits>
#include <iostream>
using namespace std;
const int N=1e5+10;
int n,q,x,ans[N],max_num,max_next_num;
int main() {
    cin>>n;

    cin>> max_num;
    for(int i=1;i<n;++i)
    {
        cin>>x;
        if(x>=max_num)
        {
            max_next_num=max_num;
            max_num=x;
        }
        else if(x>max_next_num)
        {
            max_next_num=x;
        }
        ans[i+1]=max_next_num;
    }
    cin>>q;
    while(q--)
    {
        cin>>x;
        cout<<ans[x]<<endl;
    }

    return 0;
}

五、dd爱旋转(模拟+利用规律优化)

dd爱旋转

#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
int n,q,x;
void Print(vector<vector<int>> arr)
{
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<n-1;++j)cout<<arr[i][j]<<" ";
        cout<<arr[i][n-1]<<endl;
    }
}
void Function_1(vector<vector<int>>& arr)
{
    for(int i=0;i<n;++i)
    {
        reverse(arr[i].begin(),arr[i].end());
    }
    reverse(arr.begin(),arr.end());
}
void Function_2(vector<vector<int>>& arr)
{
    reverse(arr.begin(),arr.end());
}
int main() {
    cin>>n;
    vector<vector<int>> arr(n,vector<int>(n));
    for(int i=0;i<n;++i)
        for(int j=0;j<n;++j)
            cin>>arr[i][j];

    cin>>q;
    int count_1=0,count_2=0;
    while(q--)
    {
        cin>>x;
        if(x==1)++count_1;
        else ++count_2;
    }
    if(count_1)
    {
        count_1%=2;
        if(count_1)Function_1(arr);
    }
    if(count_2)
    {
        count_2%=2;
        if(count_2)Function_2(arr);
    }

    Print(arr);

    return 0;
}

六**、小红取数(01背包+同余定理)

小红取数_牛客题霸_牛客网

最先使用dp而后以失败告终,随后

同余的记忆浮现于脑海,但是做法已经遗忘在了风中。。。

然后利用暴力的思路写了dfs,但是递归深度过大导致WA

#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=1010;
LL arr[N];
LL dp[N][N];//dp[i][j]表示从前i个人选 总和%k==j时的最大和是多少
int n,k;
int main() {
   cin>>n>>k;
   memset(dp, -0x3f3f3f3f, sizeof dp);
   dp[0][0] = 0;
   for(int i=1;i<=n;++i) cin>>arr[i];
   for(int i=1;i<=n;++i)
      for(int j=0;j<k;++j)
        dp[i][j]=max(dp[i-1][j],dp[i-1][(j-arr[i]%k+k)%k]+arr[i]);
   if(dp[n][0]<=0) cout<<-1<<endl;
   else cout<<dp[n][0]<<endl;
}

七、神奇的字母II(哈希)

神奇的字母(二)

#include<iostream>
#include<string>
using namespace std;
string s;
int main(){
    int hash[26]={0};
    char ret=0;
    int maxcount=0;
    while(cin>>s)
        for(auto&ch:s)
            if(++hash[ch-'a']>maxcount){
                maxcount=hash[ch-'a'];
                ret=ch;
            }
    cout<<ret<<endl;
}

八、字符编码(哈夫曼编码)

字符编码_牛客题霸_牛客网

由于不了解哈夫曼编码遂通过率0,其实还是很简单的

#include <iostream>
#include <queue>
#include <string>
#include <vector>
using namespace std;
string s;
int main() {
    while(cin>>s){
        //先统计每个字符的频次 然后再丢到小根堆里
        int hash[128]={0};
        for(auto&ch:s) ++hash[ch];
        //将频次丢到小根堆里
        priority_queue<int,vector<int>,greater<int>> heap;
        for(int i=0;i<128;++i) 
           if(hash[i]) heap.push(hash[i]);
        int ret=0;
        while(heap.size()>1){
            int t1=heap.top();
            heap.pop();
            int t2=heap.top();
            heap.pop();
            ret+=t1+t2;
            heap.push(t1+t2);
        }
        cout<<ret<<endl;
    }
}

九*、最少的完全平方数(完全背包)

最少的完全平方数_牛客题霸_牛客网

居然是完全背包问题,我还以为是贪心

中了贪心的陷阱

比如12这个数:用贪心来解,就是9+1+1+1 也就是4次

正确答案应该是4+4+4 3次!

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

int main() {
    int n;
    cin >> n;
    
    vector<int> dp(n + 1, INT_MAX);
    dp[0] = 0;
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j * j <= i; j++) {
            dp[i] = min(dp[i], dp[i - j * j] + 1);
        }
    }
    
    cout << dp[n] << endl;
    return 0;
}

最少完全平方数避免贪心策略

十**、游游的字母串(优化枚举)

游游的字母串

666一直以为纯暴力会超时所以没写,亏大发

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
string s;
int main(){
    cin>>s;
    int ret=0x3f3f3f3f;
    for(char i='a';i<='z';++i){
        int sum=0;//统计变化的次数
        for(auto&ch:s)
            sum+=min(abs(ch-i),26-abs(ch-i));
        ret=min(sum,ret);
    }
    cout<<ret<<endl;
}

十一、体育课测试II (BFS拓扑排序)

体育课测验(二)_牛客题霸_牛客网

#include <unordered_map>
class Solution {
public:
    vector<int> findOrder(int numProject, vector<vector<int> >& groups) {
        vector<int> in(numProject);
        unordered_map<int, vector<int>> edages;
        for(auto &arr:groups)
        {
            //b->a
            int a=arr[0],b=arr[1];
            ++in[a];
            edages[b].push_back(a);
        }

        queue<int> q;
        for(int i=0;i<numProject;++i)
        {
            if(in[i]==0)q.push(i);
        }

        vector<int> ret;
        while(!q.empty())
        {
            int sz=q.size();
            while(sz--)
            {
                int b=q.front();
                q.pop();
                ret.push_back(b);
                for(auto&a:edages[b])
                {
                    if(--in[a]==0)q.push(a);
                }
            }
        }

        for(int i=0;i<numProject;++i)
        {
            if(in[i])return {};
        }

        return ret;
    }
};

十二*、合唱队形(序列dp)

合唱队形_牛客题霸_牛客网

正着思考没思路,睡了一觉刷牙的时候突然想这不就是求最长的山峰序列的长度么。

然后又想到可以正反都弄一个dp表,每个位置的最长山峰序列就是两个dp[i]的值相加减去1.

但是最后一直通过百分之七十,因为笔试的时候看不到用例,做完后,我又找到了原题

173 132 142 161

预期输出1 ,结果输出了2,结果发现

初始化问题

应该是n+1,我#¥%¥%¥……%……¥*

#include <iostream>
#include<vector>
using namespace std;
const int N=1010;
int arr[N],n;
void Print(vector<int> arr)
{
    for(int i=1;i<n;++i)cout<<arr[i]<<" ";
    cout<<arr[n]<<endl;
}
int main() {
    cin>>n;
    for(int i=1;i<=n;++i)cin>>arr[i];


    //n+1!!!因为要用到n
    vector<int> f(n+1,1);
    auto g=f;
    
    //f[N]表示正序递增到i位置最长递增子序列的长度
    for(int i=2;i<=n;++i)
    {
        for(int j=1;j<i;++j)
        {
            if(arr[j]<arr[i])
            {
                f[i]=max(f[i],f[j]+1);
            }
        }
    }

    //g[N]表示逆序递增到i位置最长递增子序列的长度
    for(int i=n-1;i>=1;--i)
    {
        for(int j=n;j>i;--j)
        {
            if(arr[j]<arr[i])
            {
                g[i]=max(g[i],g[j]+1);
            }
        }
    }

    // Print(f);
    // Print(g);
    //计算最长“山峰”序列的长度
    int len=1;
    for(int i=1;i<=n;++i)
    {
        len=max(len,g[i]+f[i]-1); 
    }
    cout<<n-len;

    return 0;
}

十三、棋子翻转(模拟)

棋子翻转_牛客题霸_牛客网

最简单的一题

class Solution {
public:
    int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
    void change(int &x)
    {
        if(x==1)x=0;
        else x=1;
    }
    vector<vector<int> > flipChess(vector<vector<int> >& A, vector<vector<int> >& f) {
        int m=A.size();
        for(int i=0;i<f.size();++i)
        {
            //处理映射
            int x=f[i][0]-1,y=f[i][1]-1;
            for(int k=0;k<4;++k)
            {
                int nx=x+dx[k],ny=y+dy[k];
                if(nx>=0&&nx<m&&ny>=0&&ny<m)
                {
                    change(A[nx][ny]);
                }
            }
        }

        return A;
    }   
};

十四**、宵暗的妖怪(区间dp)

宵暗的妖怪

dp题让我有点崩溃。。。。

//打家劫舍问题 但是不能抢到最后一家
#include<iostream>
using namespace std;
typedef long long LL;
const int N=1e5+10;
LL arr[N],dp[N];
//dp[i]表示1-i区间的最大饱食度是多少
//dp[i-3]+arr[i]
int n;
int main(){
    cin>>n;
    for(int i=1;i<=n;++i) cin>>arr[i];
    for(int i=3;i<=n;++i) 
        dp[i]=max(dp[i-1],dp[i-3]+arr[i-1]);
    cout<<dp[n]<<endl;
}

十五**、过桥(贪心+BFS+双指针优化掉队列)

过桥

#include<iostream>
using namespace std;
const int N=2010;
int arr[N],n;

int bfs(int arr[])
{
    int ret=0,left=1,right=1;
    while(left<=right)
    {
        ++ret;
        
        int l=left,r=right;
        for(int i=left;i<=right;++i)
        {
            r=max(r,arr[i]+i);
            if(r>=n)return ret;
        }
        left=right+1;
        right=r;
    }
    
    return -1;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)cin>>arr[i];
    
    cout<<bfs(arr);
    return 0;
}

十六、最大差值

最大差值_牛客题霸_牛客网

思路一:暴力->超时

思路二:用两个数组,一个数组存0~i位置最小值,一个数组存i~n-1位置的最大值,然后遍历记录最大差值(可行,还可以优化)

#include <climits>
#include <vector>
class Solution {
public:
    int getDis(vector<int>& A, int n) {
        vector<int> MinNum(n+1),MaxNum(n+1);
        MinNum[0]=INT_MAX;
        MaxNum[n]=INT_MIN;
        for(int i=1;i<=n;++i)MinNum[i]=min(MinNum[i-1],A[i-1]);
        for(int i=n-1;i>=0;--i)MaxNum[i]=max(MaxNum[i+1],A[i]);
        //MinNum: 1~n  MaxNum: 0~n-1 A: 0~n-1
        int ret=0;
        for(int i=0;i<n;++i)
        {
            ret=max(ret,MaxNum[i]-MinNum[i+1]);
        }

        return ret;
    }
};

思路三:从前往后遍历,用一个变量存储遍历到i位置的最小值,这样往后每个位置只需贪心减去该数即可,不用0~i区间都试一次

class Solution {
public:
    int getDis(vector<int>& A, int n) {
       int ret=0;//记录最大差值
       int prevmin=A[0];//记录在这之前的最小值
       for(int i=1;i<n;++i){
          ret=max(ret,A[i]-prevmin);
          prevmin=min(prevmin,A[i]);
       }
       return ret;
    }
};

十七**、兑换零钱(完全背包)

兑换零钱_牛客题霸_牛客网

可进一步优化为一维数组

#include <iostream>
#include <cstring>
using namespace std;
//dp[i][j]表示从前i个数选  面值恰好为j的最小张数
const int N=1010,M=5010;
int n,aim;
int arr[N],dp[M];
int main() {
    cin>>n>>aim;
    memset(dp,0x3f,sizeof dp);
    dp[0]=0;
    for(int i=1;i<=n;++i) cin>>arr[i];
    for(int i=1;i<=n;++i)
       for(int j=arr[i];j<=aim;++j)
          dp[j]=min(dp[j],dp[j-arr[i]]+1);
    if(dp[aim]>=0x3f3f3f3f) cout<<-1<<endl;
    else cout<<dp[aim]<<endl;
}

十八**、小红的子串(字符种类在L到R之间的子串个数)(滑动窗口+前缀和)

小红的子串

很明显是滑动窗口题

如果L==1,那就是纯滑动窗口的思路

那么我们以1为开始,求出字符种类在[1,L-1]区间内的子串数量 再求出字符种类在 [1,R]区间内子串数量,再做差,就是字符种类在[L,R]区间内的子串数量

#include<iostream>
using namespace std;
int n,l,r;
string s;

long long find(int x)
{
    long long ret=0;
    
    //模拟哈希表
    int hash[26]={0},kinds=0,left=0,right=0;
    while(right<n)
    {
        if(hash[s[right]-'a']++ ==0)++kinds;
        while(kinds>x)
        {
            if(--hash[s[left]-'a']==0)--kinds;
            ++left;
        }
        ret+=right-left+1;
        ++right;
    }
    return ret;
}

int main()
{
    cin>>n>>l>>r>>s;
    cout<<find(r)-find(l-1);
    return 0;
}

本周笔试完结撒花/(ㄒoㄒ)/~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_dindong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值