• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
2.5 蓝桥杯练习4题

2.5 蓝桥杯练习4题

昨天忘记写题解啦,今天补上。

1.[P8687 蓝桥杯 2019 省 A] 糖果

题意:糖果店的老板一共有 \(M\) 种口味的糖果出售。为了方便描述,我们将 \(M\) 种口味编号 \(1\) ∼ \(M\)。

小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 \(K\) 颗一包整包出售。

幸好糖果包装上注明了其中 \(K\) 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。

给定 \(N\) 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。

思路:看到数据范围只有\(20\),这不是妥妥的状压\(dp\)?\(dp[i]\)表示达到状态\(i\)最少要买多少包。

先预处理出能满足的状态\(h\),置\(dp[h] = 1\)。

然后转移方程:\(dp[i|a[j]] = min(dp[i|a[j]],dp[i]+1)\)

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n,m,k;

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>n>>m>>k;
    ll dp[1<<m],a[n];
    memset(dp,127,sizeof(dp));
    for(int i = 1;i <= n; i++)
    {
        int h = 0;
        for(int j = 1;j <= k; j++)
        {
            int x; cin>>x;
            x--;
            h |= (1<<x);            
        }
        dp[h] = 1;
        a[i] = h;
    }

    for(int i = 0;i < (1<<m); i++)
    {
        for(int j = 1;j <= n; j++)
        {
            if(dp[i] < (1<<30))
                dp[i|a[j]] = min(dp[i|a[j]],dp[i]+1);
        }
    }

    if(dp[(1<<m)-1] >= 1<<30)cout<<-1<<"\n";
    else cout<<dp[(1<<m)-1]<<"\n";

    return 0;
}

2.[P8611 蓝桥杯 2014 省 AB] 蚂蚁感冒

题意:长 \(100\) 厘米的细长直杆子上有 \(n\) 只蚂蚁。它们的头有的朝左,有的朝右。

每只蚂蚁都只能沿着杆子向前爬,速度是 \(1\) 厘米 / 秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有 \(1\) 只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

思路:又来到咱们经典的蚂蚁题了。两个蚂蚁相碰会掉头,这个其实我们不用考虑掉头,我们可以当作是两只蚂蚁交换位置然后朝着原方向走。那么有多少只会被感染呢?我们以被感染的朝着左边走为例。那么该只蚂蚁左边的与它方向相反的所有蚂蚁都会被感染,它右边的和它方向相同的蚂蚁都会被感染。

注意:特别的,如果被感染的蚂蚁在两端,且朝着它的那端走的话就只有它自己,因为它不会掉头了,另一边也就不用考虑啦。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 100;
struct node
{
    int dist,dir;
}a[N];

bool cmp(node a,node b)
{
    return a.dist<b.dist;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int n; cin>>n;
    for(int i = 1;i <= n; i++)
    {
        int x; cin>>x;
        a[i].dist = abs(x),a[i].dir = (x>0?1:-1);
    }
    int ill = a[1].dist,d = a[1].dir;
    sort(a+1,a+1+n,cmp);
    int k = 1;
    for(int i = 1;i <= n; i++)
    {
        if(a[i].dist == ill)
        {
            k = i;
            break;
        }
    }
    int cnt = 0;

    if(d < 0)//左
    {
        for(int i = 1;i <= k-1; i++)
            cnt += (d !=a[i].dir);
        
        if(cnt){//!!!小心这里
             for(int i = k + 1;i <= n; i++)
                cnt += (d == a[i].dir);
        }
    }
    else
    {
        for(int i = k + 1;i <= n; i++)
            cnt += (d != a[i].dir);
       
        if(cnt){
            for(int i = 1;i <= k-1; i++)
                cnt += (d == a[i].dir);
        }
        
    }
    cout<<cnt+1<<"\n";
    return 0;
}

3.[P8638 蓝桥杯 2016 省 A] 密码脱落

题意:X 星球的考古学家发现了一批古代留下来的密码。

这些密码是由 A、B、C、D 四种植物的种子串成的序列。

仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的回文串)。

由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

你的任务是:

给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

思路:其实很简单,我们只要把原串\(s\)反转一下变成\(t\),求\(s\)和\(t\)的最长回文子串的长度,然后用\(n-最长回文子串的长度\)就是答案啦。这里求最长回文子串的长度我用的是记忆化搜索的版本。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e3 + 10;
int f[N][N],n,m;
string s,t;
int lcs(int i,int j)
{
    if(f[i][j] != -1)return f[i][j];
    if(i==0||j==0)return 0;
    else if(s[i]==t[j])f[i][j] = lcs(i-1,j-1) + 1;
    else f[i][j] = max(lcs(i,j-1),lcs(i-1,j));
    return f[i][j];
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    cin>>s;
    t = s;
    n = s.size();
    reverse(t.begin(),t.end());
    s = "?" + s,t = "?" + t;
    memset(f,-1,sizeof(f));
    cout<<n-lcs(n,n)<<"\n";

    return 0;
}

4.[P8646 蓝桥杯 2017 省 AB] 包子凑数

题意:小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 \(N\) 种蒸笼,其中第 \(i\) 种蒸笼恰好能放 \(A_i\) 个包子。每种蒸笼都有非常多笼,可以认为是无限笼。

每当有顾客想买 \(X\) 个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有 \(X\) 个包子。比如一共有 \(3\) 种蒸笼,分别能放 \(3\) 、 \(4\) 和 \(5\) 个包子。当顾客想买 \(11\) 个包子时,大叔就会选 \(2\) 笼 \(3\) 个的再加 \(1\) 笼 \(5\) 个的(也可能选出 \(1\) 笼 \(3\) 个的再加 \(2\) 笼 \(4\) 个的)。

当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有 \(3\) 种蒸笼,分别能放 \(4\) 、 \(5\) 和 \(6\) 个包子。而顾客想买 \(7\) 个包子时,大叔就凑不出来了。

小明想知道一共有多少种数目是包子大叔凑不出来的。

思路:由裴蜀定理可知\(ax+by = c\)。如果\(c = t\times\gcd(a,b)\)。只有\(\gcd(a,b)=1\)的时候才能表示所有的数。那么我们可以求一下所有数的\(\gcd\)的多少,如果不是\(1\),那么答案就是\(INF\)。那么对于是\(1\)的情况,有多少是不能表示的呢?这里可以用完全背包啦。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int a[N],dp[N],V = 1e4;
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int n; cin>>n;
    for(int i = 1;i <= n; i++)
        cin>>a[i];
    sort(a+1,a+1+n);
    //ax + by = gcd(a,b);
    int g = a[1];
    for(int i = 1;i <= n; i++)
    {
        g = __gcd(g,a[i]);
    }
    if(g != 1)cout<<"INF\n";
    else{
        dp[0] = 1;
        for(int i = 1;i <= n; i++)
        {
            for(int j = a[i];j <= V; j++)
            {
                dp[j] = max(dp[j],dp[j-a[i]]);
            }
        }
        int cnt = 0;
        for(int i = 1;i <= V; i++)
        {
            if(!dp[i])cnt++;
        }
        cout<<cnt<<"\n";
    }
    return 0;
}

posted on 2024-02-06 22:26  nannandbk  阅读(36)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3