笔试强训:Week -8

目录

一、kotori和抽卡(二)(组合数Cmn)

二**、ruby和薯条(排序  +   二分 /滑动窗口+前缀和 )

三**、循环汉诺塔(递推)

四、最小差值(排序)

五**、kotori和素因子(枚举+dfs)

六**、dd爱科学1.0(贪心+二分)

七、kanan和高音(双指针)

八、拜访(单源bfs最短路)

九*、买卖股票的最佳时机IV(多状态dp,"k次交易"注意初始化)

十、AOE还是单体?(贪心)

十一、kotori和n皇后(哈希)

十二**、取金币(区间dp)

十三、矩阵转置

十四**、四个选项(枚举+dfs+剪枝+哈希)

十五、接雨水(双指针)

十六、疯狂的自我检索者(贪心)

十七、栈和排序(贪心+栈)

十八**、加减(排序+贪心+滑动窗口)


一、kotori和抽卡(二)(组合数Cmn)

kotori和抽卡(二)

今天刚做组合数的模板题,思路被干扰了,这里数据量这么小还是返璞归真

#include<iostream>
#include<cmath>
using namespace std;
//3次出两张  c32*p^2*(1-p)^1 
int n,m;
int main(){
    cin>>n>>m;
    double ret=1.0;
    for(int i=n;i>n-m;--i) ret*=i;
    for(int i=m;i>=2;--i) ret/=i;
    printf("%.4f",ret*pow(0.8,m)*pow(0.2,n-m));
}

二**、ruby和薯条(排序  +   二分 /滑动窗口+前缀和 )

ruby和薯条

首先很自然的能想到能对数组排序,然后去找配对

这里配对需要暴力找吗?比如固定一个数,然后去前边暴力查找区间?注意到数组已经排序了,针对排序数组最好的查找算法不就是二分,做题一定要想到这点。

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int n,l,r;
const int N=2e5+10;
int arr[N];
//排序+二分
int main(){
    cin>>n>>l>>r;
    for(int i=0;i<n;++i) cin>>arr[i];
    sort(arr,arr+n);
    LL ret=0;
    for(int i=1;i<n;++i){//从第二个数开始枚举 1 2 3 5 6   
        int left=0,right=i-1;
        int L,R;//记录左端点和右端点
        while(left<right){
            int mid=left+(right-left)/2;
            if(arr[mid]<arr[i]-r) left=mid+1;
            else right=mid;
        }
       //这种情况就是可能找到的数字也是不满足的
        if(arr[left]>=arr[i]-r) L=left;
        else L=left+1;
        left=0,right=i-1;//找右端点
        while(left<right){
            int mid=left+(right-left+1)/2;
            if(arr[mid]<=arr[i]-l) left=mid;
            else right=mid-1;
        }
         //这种情况就是可能找到的数字也是不满足的
        if(arr[left]<=arr[i]-l) R=left;
        else R=left-1;
        if(R>=L) ret+=R-L+1;
    }
    cout<<ret;
    return 0;
}

解法二就是模拟前几天刷的滑动窗口那题,用前缀和的思想f[l]-f[r-1],感觉有点偏暴力了

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int n,l,r;
const int N=2e5+10;
int arr[N];
LL find(int x){
    LL ret=0;
    for(int left=0,right=0;right<n;++right){
        while(arr[right]-arr[left]>x) ++left;
        ret+=right-left;
    }
    return ret;
}
int main(){
    cin>>n>>l>>r;
    for(int i=0;i<n;++i) cin>>arr[i];
    sort(arr,arr+n);
    cout<<find(r)-find(l-1)<<endl;
}

三**、循环汉诺塔(递推)

循环汉诺塔_牛客题霸_牛客网

毫无思路的一题

遇到这种情况,还是先举几个小的自己模拟

#include <iostream>
using namespace std;
const int MOD=1e9+7;
int main() {
    int n;cin>>n;
    int x=1,y=2;
    for(int i=2;i<=n;++i)
    {
        int tmpx=x,tmpy=y;
        x=(2*tmpy+1)%MOD;
        y=(2*tmpy%MOD+tmpx+2)%MOD;
    }
    cout<<x<<" "<<y;
    return 0;
}

四、最小差值(排序)

最小差值

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,arr[N];
int main() {
    cin>>n;
    for(int i=0;i<n;++i)cin>>arr[i];

    sort(arr,arr+n);
    int gap=0x3f3f3f3f;
    for(int i=0;i<n-1;++i)
    {
        if(arr[i+1]-arr[i]<gap) gap=arr[i+1]-arr[i];
    }
    cout<<gap;
    return 0;
}

五**、kotori和素因子(枚举+dfs)

居然是直接dfs,我服了,果然想不出最优算法的时候就要退其次,先看暴力

kotori和素因子

#include<bits/stdc++.h>
using namespace std;
int n,sum=INT_MAX;
bool check[1000];
void dfs(vector<vector<int>>& prime,int pos,int path)
{
    if (pos==n)
    {
        sum=min(sum,path);
        return;
    }
    int sz=prime[pos].size();
    for (int i=0;i<sz;i++)
    {
        if (!check[prime[pos][i]])
        {
            check[prime[pos][i]]=true;
            dfs(prime,pos+1,path+prime[pos][i]);
            check[prime[pos][i]]=false;
        }
    }
}
int main()
{
    cin>>n;
    vector<vector<int>> prime(n);
    //存储质因子个数
    unordered_map<int,int> PrimeCount;
    int cnt=0,num;
    while (cnt<n)
    {
        cin>>num;
        if (num%2==0)
        {
            prime[cnt].push_back(2);
            ++PrimeCount[2];
            while (num%2==0)num/=2;
        }
        for (int i=3;i*i<=num;i+=2)
        {
            if (num%i==0)
            {
                prime[cnt].push_back(i);
                ++PrimeCount[i];
                while (num%i==0)num/=i;
            }
        }
        if (num>1)
        {
            prime[cnt].push_back(num);
            ++PrimeCount[num];
        }
        ++cnt;
    }
    //dfs
    if (PrimeCount.size()<n)sum=-1;
    else dfs(prime,0,0);

    cout<<sum;
    return 0;
}

六**、dd爱科学1.0(贪心+二分)

dd爱科学1.0

其实是查找最长递增子序列,记长度为L,答案为n-L

可以用动规做,最优解法是贪心+二分

#include<iostream>
#include<vector>
using namespace std;
string s;
int n;
//最长非下降子序列
int main(){
    cin>>n>>s;
    vector<char> v;
    for(int i=0;i<n;++i){
        char ch=s[i];
        if(v.size()==0||ch>=v.back()) v.emplace_back(ch);
        else{
            //开始二分
            int left=0,right=v.size()-1;
            while(left<right){
                int mid=left+(right-left)/2;
                if(v[mid]<=ch) left=mid+1;
                else right=mid;
            }
            v[left]=ch;
        }
    }
    cout<<n-v.size()<<endl;
    return 0;
}

七、kanan和高音(双指针)

kanan和高音

#include <iostream>
using namespace std;
const int N=2e5+10;
int n,arr[N];
int main() {
    cin>>n;
    for(int i=0;i<n;++i)cin>>arr[i];

    int len=1,left=0,right=0;
    while(right<n)
    {
        while(right+1<n&&arr[right+1]-arr[right]<9)++right;
        len=max(len,right-left+1);

        ++right;
        left=right;
    }
    cout<<len;

    return 0;
}

八、拜访(单源bfs最短路)

拜访_牛客题霸_牛客网

#include <vector>
class Solution {
private:
    int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
public:
    int countPath(vector<vector<int> >& CityMap, int n, int m) {
        queue<pair<int,int>> q;
        for(int i=0;i<n;++i)
        {
            bool flag=false;
            for(int j=0;j<m;++j)
            {
                if(CityMap[i][j]==1)
                {
                    q.push({i,j});
                    flag=true;
                    break;
                }
            }
            if(flag)break;
        }

        int ret=0;
        vector<vector<bool>> vis(n,vector<bool>(m));
        while(!q.empty())
        {
            //记录此层元素个数
            int sz=q.size();
            bool flag=false;
            while(sz--)
            {
                auto [i,j]=q.front();
                q.pop();
                //标记已经经过的地方
                vis[i][j]=true;
                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&&CityMap[x][y]!=-1&&!vis[x][y])
                    {
                        if(CityMap[x][y]==2)
                        {
                            //此时是最短路线,等待此层其他能到达的,统计
                            ++ret;
                            flag=true;
                            //中断该点的探查
                            break;
                        }
                        q.push({x,y});
                    }
                }
            }
            //找到了最短路线,退出
            if(flag)break;
        }
        return ret;
    }
};

九*、买卖股票的最佳时机IV(多状态dp,"k次交易"注意初始化)

买卖股票的最好时机(四)_牛客题霸_牛客网

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

    //f买入 g可交易
    vector<vector<int>> f(n+1,vector<int>(k+1));
    auto g=f;

    int money=0;
    for (int j=0;j<=k-1;++j)f[0][j]=-prices[1];
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<=k;++j)
        {
            f[i][j]=max(f[i-1][j],g[i-1][j]-prices[i]);
            g[i][j]=g[i-1][j];
            if(j>=1)g[i][j]=max(g[i][j],f[i-1][j-1]+prices[i]);
        }
    }

    for(int j=1;j<=k;++j)money=max(money,g[n][j]);
    cout<<money;

    return 0;
}

十、AOE还是单体?(贪心)

AOE还是单体?

#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int arr[N];
int n,x;
int main(){
    cin>>n>>x;
    for(int i=1;i<=n;++i) cin>>arr[i];
    sort(arr+1,arr+1+n);
    long long ret=0;
    int index=max(n-x,0);
    ret+=arr[index]*x;
    for(int i=index+1;i<=n;++i) ret+=arr[i]-arr[index];
    cout<<ret<<endl;
    return 0;
}

十一、kotori和n皇后(哈希)

kotori和n皇后

N皇后经典的就是判断条件hh

#include <iostream>
#include<unordered_map>
using namespace std;
using ll=long long;
const int M=1e5+10;
int k,x,y,t,i;

int main() {
    //Dig1主对角线 Dig2副对角线
    unordered_map<ll,int> R,C,Dig1,Dig2;
    cin>>k;
    int flag=M,cnt=1;
    while(cnt<=k)
    {
        cin>>x>>y;
        //因为还要输入,不能找到后直接break
        if (flag!=M)
        {
            ++cnt;
            continue;
        }
        if(R.count(x)||C.count(y)||Dig1.count(y-x)||Dig2.count(y+x))
        {
            //寻找第一次出现冲突的那一个,后边冲突一直存在
            flag=cnt;
        }
        ++R[x],++C[y],++Dig1[y-x],++Dig2[y+x];
        ++cnt;
    }

    cin>>t;
    while(t--)
    {
        cin>>i;
        if(i<flag)cout<<"No";
        else cout<<"Yes";

        cout<<'\n';
    }
    return 0;
}

十二**、取金币(区间dp)

取金币_牛客题霸_牛客网

class Solution {
public:
    int arr[110]={0};
    int dp[110][110]={0};
    int getCoins(vector<int>& coins) {
        //dp[i][j] i-j区间的最大积分
        int n=coins.size();
        arr[0]=arr[n+1]=1;
        for(int i=1;i<=n;++i) arr[i]=coins[i-1];
        for(int i=n;i>=1;--i)
            for(int j=i;j<=n;++j)
                for(int k=i;k<=j;++k)
                    dp[i][j]=max(dp[i][j],dp[i][k-1]+dp[k+1][j]+arr[k]*arr[i-1]*arr[j+1]);
        return dp[1][n];
    }
    return 0;
};

十三、矩阵转置

矩阵转置_牛客题霸_牛客网

修改输出逻辑即可

#include <iostream>
using namespace std;
int arr[15][15];
int main() {
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)cin>>arr[i][j];
        
    for(int j=0;j<m;++j)
    {
        for(int i=0;i<n-1;++i)
        {
            cout<<arr[i][j]<<" ";
        }
        cout<<arr[n-1][j]<<"\n";
    }

    return 0;
}

十四**、四个选项(枚举+dfs+剪枝+哈希)

四个选项

dfs题并不难,考察的是编码能力

#include<iostream>
#include<vector>
using namespace std;
int m,x,y;
bool same[13][13];//标记两题的选项是否相同
int ret=0;
int cnt[5];//记录每个选项的次数
vector<int> path;//记录过程中已经选过了哪些选项  1-12号位置分别代表1-12题 1-4分别代表具体选项
bool issame(int pos,int cur){//pos代表之前的位置 cur是我们期望填入的选项
    for(int i=1;i<pos;++i)
        if(same[pos][i]&&path[i]!=cur) return false;
    return true;
}
void dfs(int pos){
    if(pos>12){
        ++ret;
        return;
    }
    //此时开始尝试填选项
    for(int i=1;i<=4;++i)
        if(cnt[i]&&issame(pos,i)){
            path.emplace_back(i);
            --cnt[i];
            dfs(pos+1);
            path.pop_back();
            ++cnt[i];
        }
}
int main(){
    for(int i=1;i<=4;++i) cin>>cnt[i];
    cin>>m;
    while(m--){
        cin>>x>>y;
        same[x][y]=same[y][x]=true;//表示两个选项必须相同
    }
    path.emplace_back(0);//占位置
    dfs(1);
    cout<<ret<<endl;
    return;
}

十五、接雨水(双指针)

42. 接雨水 - 力扣(LeetCode)

左右指针,记录更小的,更小为始往中间逼近,遇到比记录的小值小的,+=差值,否则更新小值,进入下一轮,直到指针相遇

class Solution {
public:
    int trap(vector<int>& arr) {
        int n=arr.size();
        if(n<=2)return 0;

        int right=n-1,left=0,ret=0;
        while(left<right)
        {
            if(arr[left]<arr[right])
            {
                int tmp=arr[left];
                while(arr[left+1]<tmp)
                {
                    ret+=(tmp-arr[left+1]);
                    ++left;
                }
                //arr[left+1]>tmp更新端点
                ++left;
            }
            else{
                int tmp=arr[right];
                while(arr[right-1]<tmp)
                {
                    ret+=(tmp-arr[right-1]);
                    --right;
                }
                --right;
            }
        }
        return ret;
    }
};

十六、疯狂的自我检索者(贪心)

疯狂的自我检索者

#include <iostream>
#include<iomanip>
using namespace std;
int n,m,x;
int main() {
    cin>>n>>m;
    double ans=0;
    for(int i=0;i<n-m;++i)
    {
        cin>>x;
        ans+=x;
    }
    double tmp=ans;
    for(int i=0;i<m;++i)ans+=1;
    cout<<fixed<<setprecision(5)<<ans/n<<" ";
    ans=tmp;
    for(int i=0;i<m;++i)ans+=5;
    cout<<fixed<<setprecision(5)<<ans/n;
    return 0;
}

十七、栈和排序(贪心+栈)

栈和排序_牛客题霸_牛客网

class Solution {
public:
    vector<int> solve(vector<int>& a) {
        vector<int> ret;
        int n=a.size();
        if(n==0)return ret;

        int cur=0;
        stack<int> st;
        while(ret.empty()&&a[cur]!=n)
        {
            st.push(a[cur]);
            ++cur;
        }
        ret.push_back(a[cur]);
        ++cur;

        while(cur<n)
        {
            int max_num=st.empty()?0:st.top();
            for(int i=cur;i<n;++i)max_num=max(max_num,a[i]);
            while(cur<n&&(st.empty()||st.top()!=max_num))
            {
                st.push(a[cur++]);
            }
            ret.push_back(st.top());
            st.pop();
        }
        while(!st.empty())
        {
            ret.push_back(st.top());
            st.pop();
        }
        return ret;
    }
};

十八**、加减(排序+贪心+滑动窗口)

加减

难在滑动窗口的更新条件,综合起来看是很好的一道题了

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
typedef long long LL;
LL n,k,arr[N],sum[N];  // sum是前缀和数组
LL cal(int left,int right){
    //这个函数统计把该区间都变成相同的最小代价
    int mid=(left+right)>>1;
    return (mid-left-right+mid)*arr[mid]-(sum[mid-1]-sum[left-1])+(sum[right]-sum[mid]);
}
 
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;++i) cin>>arr[i];
    //排序+前缀和数组的预处理
    sort(arr+1,arr+1+n);
    for(int i=1;i<=n;++i) sum[i]=sum[i-1]+arr[i];
    int ret=1;
    for(int left=1,right=1;right<=n;++right){
        while(cal(left,right)>k) ++left;
        ret=max(ret,right-left+1);
    }
    cout<<ret;
    return 0;
}

至此,笔试强训全部结束。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_dindong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值