【笔试训练】day24-day48合集(2)

简介: 【笔试训练】day24-day48合集(2)

【笔试训练】day24-day48合集(1):https://developer.aliyun.com/article/1515228




day30

一、爱吃素

两个大于0的数相乘,如果没有1 ,那么一定是非质数。如果有1,就看对方是不是质素。数据太大,不能直接相乘后计算是否是质数。

代码:

#include <iostream>
using namespace std;
typedef long long LL;
 
bool isprem(LL x){
    if(x==1)return false;
    for(int i=2;i<=x/i;i++){
        if(x%i==0)return false;
    }
    return true;
}
 
int main() {
    int t;
    cin>>t;
    while(t--){
        LL a,b;
        cin>>a>>b;
        if((a==1&&isprem(b))||(b==1&&isprem(a))){
             cout<<"YES"<<endl;
        }else{
            cout<<"NO"<<endl;
        }
    }
    
    return 0;
}

二、相差不超过k的最多数

排序后二分。枚举每一个数,看如果这个数字作为选出组合的最小数,那这个组合最大能选的数的坐标是多少。

代码:

#include <iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int a[N];
int main() {
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++)cin>>a[i];
    sort(a,a+n);
    int ans=0;
    for(int i=0;i<n;i++){
        int l=i-1;
        int r=n;
        while(l+1!=r){
            int mid=(l+r)>>1;
            if(a[mid]<=a[i]+k)l=mid;
            else r=mid;
        }
        ans=max(l-i+1,ans);
    }
    cout<<ans<<endl;
    return 0;
}

三、最长公共子序列(一)

动态规划板子题。

朴素做法。用f[i][j]表示字符串a的前i个字符和b的前j个字符里面,最长公共子序列是多少。

如果a[i]==b[i]那么f[i][j]=f[i-1][j-1]否则上一个f[i][j]只能由上一个状态f[i-1][j]或者f[i][j-1]中转移过来。

代码:

#include <iostream>
using namespace std;
const int N=1100;
int f[N][N];
int main() {
   int n,m;
   cin>>n>>m;
 
   string a,b;
   cin>>a>>b;
   for(int i=1;i<=a.size();i++){
    for(int j=1;j<=b.size();j++){
        if(a[i-1]==b[j-1]){
            f[i][j]=f[i-1][j-1]+1;
        }else{
            f[i][j]=max(f[i-1][j],f[i][j-1]);
        }
        //cout<<f[j]<<endl;
      
    }
   }
   cout<<f[n][m]<<endl;
 
   return 0;
}

再思考进阶做法。进阶做法时间复杂度没有改变,也就是优化空间复杂度。看能不能将二维f改成一维。假设b字符串是较小的那个。f[i][j]的状态只由上一个状态转移过来(f[i-1][j]或者f[i][j-1]),除了相等的时候。 于是我们可以用一个变量存f[i-1][j-1],这个变量的值严格上升,再去掉f[i]这一维,没有影响。

#include <iostream>
using namespace std;
const int N=1100;
int f[N];
int main() {
   int n,m;
   cin>>n>>m;
 
   string a,b;
   cin>>a>>b;
   if(a.size()<b.size())swap(a,b);
   int p=0;
   for(int i=1;i<=a.size();i++){
    for(int j=1,p=0;j<=b.size();j++){
        int t=f[j];
        if(a[i-1]==b[j-1]){
            f[j]=p+1;
        }else{
            f[j]=max(f[j],f[j-1]);
        }
        //cout<<f[j]<<endl;
        p=t;
    }
   }
   cout<<f[b.size()]<<endl;
 
   return 0;
}

day31

一、小红的口罩

贪心。每次都戴最舒服的口罩,即不舒适度最低的口罩。用完这个值就*2。保证每次用最舒服的口罩,可以使用最小堆。

代码:

#include <iostream>
#include<queue>
using namespace std;
const int N=1e5+10;
int a[N];
 
int main() {
    int n,k;
    cin>>n>>k;
    priority_queue<int,vector<int>,greater<int>> q;
    for(int i=0;i<n;i++){
        cin>>a[i];
        q.push(a[i]);
    }
 
    int ans=0;
 
    while(!q.empty()&&k>0){
        int t=q.top();
        q.pop();
        
        k-=t;
        if(k>=0)ans++;
        q.push(t*2);
    }
 
    cout<<ans<<endl;
    return 0;
}

二、春游

这题老坑了。

贪心。总和费用最少,那就是人均最少。比较双人船和三人船的人均费用。谁费用少就优先考虑谁。直到最后会剩下几个人,可能剩下1或者2个人。

如果大部分用的是双人船,最后剩下的这个人要么自己坐双人,要么和之前坐双人船的队友拉下来坐一个三人船。

如果大部分用的是三人船,最后会剩下1或者2人。如果是一个人,这个人可以选择坐三人船,也可以选择拉下一队三人船的队友,一起坐两艘双人船。如果是两个人,那么这两个人可以一起坐三人,也可以一起坐双人船。

最后值得注意的是,如果n的值很小,在上面的方案中要避免出现答案为负数的情况。

代码

#include <iostream>
using namespace std;
typedef long long LL;
int main() {
    int t;
    cin>>t;
    while(t--){
       LL n,a,b;
       cin>>n>>a>>b;
       LL ans=0;
       if (n <= 2) {
         cout<<min(a,b)<<endl;
       continue;
       }
       if(3*a>=2*b){
           int k=n%3;
           ans+=n/3*b;
           if(k==2)ans+=min(a,b);
           else if(k==1){
            ans+=min(min(a,b),2*a-b);
           }
       }else{
           int k=n%2;
           ans+=n/2*a;
           if(k){
            ans+=min(a,b-a);
           }
       }
       cout<<ans<<endl;
    }
 
 
   return 0;
}
 

三、数位染色

暴力枚举或者dp。把所有数位拿出来,看能不能选一堆数凑成总和的一半。用dp做的话就是用dp[j]表示是否存在凑成剩余容量j的方案。这里不多讲。我的做法是用二进制位表示某一个位数是否染色,由于数位总数最大18。枚举完所有状态就是2的18次方。

代码:

#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
   string str;
   cin>>str;
   vector<int> a;
   int s=0;
   for(int i=0;i<str.size();i++){
    int u=str[i]-'0';
    s+=u;
    a.push_back(u);
   }
   if(s%2){
    cout<<"No"<<endl;
    return 0;
   }
   int n=a.size();
   int f=0;
   int ma=(1<<(n+1))-1;
   for(int i=1;i<ma;i++){
    int t=0;
    for(int j=0;j<n;j++){
        if((i>>j)&1)t+=a[j];
    }
    if(t==s/2){
        f=1;
        break;
    }
   }
   if(f)cout<<"Yes"<<endl;
   else cout<<"No"<<endl;
   return 0;
}

day32

一、素数回文

用字符串模拟一遍,注意long long

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
 
bool isprime(long long x) {
    if (x == 1)return false;
    for (int i = 2; i <= x / i; i++) {
        if (x % i == 0)return false;
    }
    return true;
}
 
int main() {
    string str;
    cin >> str;
    int n = str.size();
    for (int i = n - 2; i >= 0; i--) {
        str.push_back(str[i]);
    }
    long long ans = 0;
    for (int i = 0; i < str.size(); i++) {
        int u = str[i] - '0';
        ans = ans * 10 + u;
    }
    if (isprime(ans)) {
        cout << "prime" << endl;
    }
    else {
        cout << "noprime" << endl;
    }
    return 0;
}

二、活动安排

贪心。先对所有区间进行排序,结束时间越早的排在前面,结束时间相等,那就开始时间越大的排在前面。然后枚举所有区间,如果当前活动的开始时间大于上一个加入答案活动的结束时间,ans++.

代码:

#include <iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
 
bool cmp(PII& A,PII& B){
    if(A.second==B.second){
        return A.first>B.first;
    }
    return A.second<B.second;
}
 
int main() {
    int n;
    cin>>n;
    vector<PII> v(n);
   for(int i=0;i<n;i++){
    cin>>v[i].first>>v[i].second;
   }
 
   sort(v.begin(),v.end(),cmp);
   int ans=1;
   int t=v[0].second;
   for(int i=1;i<n;i++){
       if(v[i].first>=t){
        t=v[i].second;
        ans++;
       }
   }
   cout<<ans<<endl;
    return 0;
}

三、合唱团

背包问题。每个人都有选和不选两种状态。

f[i][j]表示以第i个人结尾,选出j个人的最大值。由于相邻位置差小于d。所以f[i][j]这个状态从i的前d个人选 j-1个人的状态转移过来。所以f[i][j]=max(f[z][j-1],f[i]][j])。z表示i的前d个人中的某一个。

但值得注意的是,题目给的数据有负数。有负数会有什么影响呢?由于一个越大的数乘一个负数会变得越小。且一个越小的数,乘以一个负数,就会变得越大。对于f[i][j]来说,它不仅可能由f[z][j-1]转移过来,也有可能从minf[z][j-1]转移过来(minf[z][j]表示以第z个人结尾,选出j个人的最小值)。即假设第i个人是一个负数,则从minf[z][j-1]转移过来。

由于f[][]和minf[][]的含义类似,我们在更新f的同时可以顺便更新minf。

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <climits>
#include <iostream>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 1100;
LL a[N];
 
int main() {
    int n, k, d;
    cin >> n >> k >> d;
    for (int i = 0; i < n; i++)cin >> a[i];
    vector<vector<LL>> minf(n + 1, vector<LL>(k + 1));
    vector<vector<LL>> maxf(n + 1, vector<LL>(k + 1));
    for (int i = 0; i < n; i++) {
        minf[i][1] = a[i];
        maxf[i][1] = a[i];
    }
    for (int i = 0; i < n; i++) {
        for (int j = 2; j <= k; j++) {
            for (int z = max(0, i - d); z < i; z++) {
                maxf[i][j] = max(max(maxf[z][j - 1] * a[i], minf[z][j - 1] * a[i]), maxf[i][j]);
                minf[i][j] = min(min(maxf[z][j - 1] * a[i], minf[z][j - 1] * a[i]), minf[i][j]);
            }
        }
    }
    LL ans = maxf[0][k];
    for (int i = 1; i < n; i++) {
        ans = max(ans, maxf[i][k]);
    }
    cout << ans << endl;
    return 0;
}

day33

一、跳台阶扩展问题

找规律。青蛙一次可以跳【1~n】级台阶。从最后一步看,跳到第n级台阶可以从起点跳,也可以从第1级、2、3、4、...n-1。即f[n]=f[n-1]+f[n-2]+...f[1]+f[0]。f[n]表示跳到第n级台阶的方法数。找下规律。很容易看出来,f[n]=2^n-1。

代码:

#include <iostream>
using namespace std;
 
int main() {
   int n;
   cin>>n;
   cout<<(1<<(n-1));
   return 0;
}

二、包含不超过两种字符的最大子串。

滑动窗口。维护一个区间,这个区间的字符种类不超过2.用一个数组表示当前区间每种字符大的数量。遍历到当前字符就加加,如果加加后为1,说明有新种类的字符出现。用一个遍历维护种类数,其实就是字符数量大于0的个数。出现矛盾后从左区间缩小区间,字符数量减1,为0说明消失一个字符种类。

代码:

#include <iostream>
using namespace std;
 
int a[200];
int main() {
    string str;
    cin>>str;
    int n=str.size();
    int ans=0;
    int cnt=0;
    for(int i=0,j=0;i<n;i++){
       a[str[i]]++;
       if(a[str[i]]==1)cnt++;
       while(j<i&&cnt>2){
        a[str[j]]--;
        if(a[str[j]]==0)cnt--;
        j++;
       }
       ans=max(ans,i-j+1);
    }
    cout<<ans<<endl;
 
    return 0;
}

三、字符串的排列

dfs求全排列。由于字符串可能有重复的字符,对下标进行全排列会出现重复排列的可能。如“aa".

所以可以在dfs的时候检查当前位置的字符是否重复遍历过,或者下标全排列之后排序去重。我这里是后者。

代码:

class Solution {
public:
    void dfs(int u,vector<string>& ans,string t,int n,string str,vector<bool>& st){
        if(u==n){
            ans.push_back(t);
            return ;
        }
        for(int i=0;i<n;i++){
            if(!st[i]){
                st[i]=true;
                t.push_back(str[i]);
                dfs(u+1,ans,t,n,str,st);
                st[i]=false;
                t.pop_back();
            }
        }
    }
    vector<string> Permutation(string str) {
       int n=str.size();
       vector<bool> st(300,false);
       vector<string> ans;
       string t="";
       dfs(0,ans,t,n,str,st);
       sort(ans.begin(),ans.end());
       ans.erase(unique(ans.begin(),ans.end()),ans.end());
       return ans;
    }
};


相关文章
|
24天前
【笔试训练】day21
【笔试训练】day21
|
24天前
【笔试训练】day22
【笔试训练】day22
|
24天前
|
并行计算
【笔试训练】day14
【笔试训练】day14
|
24天前
【笔试训练】day23
【笔试训练】day23
|
24天前
|
人工智能
【笔试训练】day20
【笔试训练】day20
|
24天前
|
算法 Shell
【笔试训练】day12
【笔试训练】day12
|
24天前
【笔试训练】day16
【笔试训练】day16
|
10天前
笔试训练2
笔试训练2
|
24天前
|
人工智能 调度
【笔试训练】day24-day48合集(1)
【笔试训练】day24-day48合集(1)
|
24天前
【笔试训练】day15
【笔试训练】day15

热门文章

最新文章