蓝桥杯提单day2【题解】

题单链接

蓝桥杯题单day2【题目】_奔跑的星黛露的博客-CSDN博客

题解

蓝肽子序列

链接

https://www.lanqiao.cn/problems/1030/learning/?page=1&first_category_id=1&sort=students_count&second_category_id=3&tags=%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92,%E5%9B%BD%E8%B5%9B

思路

先将字符串预处理分割为每个大写字母开头的“蓝肽” 在将其作为判断最长子序列 判断最长子序列 可以采用 动态规划(最简单高效) if S1[i] = S2[j] dp[i][j] = dp[i][j]+1; else dp[i][j] = max(dp[i-1][j] , dp[i][j-1]) 输出最大值即可。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 1000;
int dp[N][N];
string a,b;
vector<string>tmp;
vector<string>w;

int main(){
    cin >> a >> b;
    for(int i = 0, j = 0 ; i < a.size();){
        if(a[j] >= 'A' && a[j] <= 'Z'){
            j++;
            while(a[j] >= 'a' && a[j] <= 'z' && j < a.size())j++;
            tmp.push_back(a.substr(i,j - i));
            i = j;
        }
    }
    for(int i = 0, j = 0 ; i < b.size();){
        if(b[j] >= 'A' && b[j] <= 'Z'){
            j++;
            while(b[j] >= 'a' && b[j] <= 'z' && j < b.size())j++;
            w.push_back(b.substr(i,j - i));
            i = j;
        }
    }
  
    int ans = 0;
    int n = tmp.size(),m = w.size();
    for(int i = 1; i <= n ; i++){
        for(int j = 1 ; j <= m; j++){
            if(tmp[i - 1] == w[j - 1])dp[i][j] = dp[i - 1][j - 1] + 1;
            else dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
        }
    }

    cout << dp[n][m] << endl;
    return 0;
}

能量项链

链接

https://www.luogu.com.cn/problem/P1063

思路

考虑区间DP。

定义 fij 表示将区间 [i, j] 合并的最大能量。那么我们可以有简单的转移方程:

f[i, j] = max_{i <= k <= j} {f[i, k] + f[k, j] + w[l] * w[k] * w[r]}

上述转移方程适用于一条链的转移,但是本题我们是一个环,因此我们可以使用一个常用的技巧将环拆解成两倍长度的链,所以 答案就是 max_{1 <= i  <= n} f[i, i + n - 1]

代码

#include "bits/stdc++.h"
using namespace std;
const int N=220;
int n,g[N],f[N][N];

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>g[i];
        g[i+n]=g[i];
    }
    for(int len=3;len<=n+1;len++){
        for(int l=1;l+len-1<=2*n;l++){
            int r=l+len-1;
            for(int k=l+1;k<r;k++){
                f[l][r]=max(f[l][r],f[l][k]+f[k][r]+g[l]*g[k]*g[r]);
            }
        }
    }
    int res=0;
    for(int l=1;l<=n;l++) res=max(res,f[l][l+n]);
    cout<<res<<endl;
    return 0;
}

字串

链接

 https://www.luogu.com.cn/problem/P2679

思路

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define CaseT int CaseT; cin >> CaseT; while(CaseT--)
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
const int INF = 0x3f3f3f3f;

const int MOD = 1e9 + 7;

void solve() {
    int n, m, K; string a, b; cin >> n >> m >> K >> a >> b;

    vector<vector<int>> dp(m + 1, vector<int>(K + 1));
    dp[0][0] = 1;
    vector<vector<int>> pre(m + 1, vector<int>(K + 1));
    for (int i = 1; i <= n; i++) {
        for (int j = m; j; j--) {  // 从后往前匹配a[1 ... i]和b[1 ... j]
            for (int k = K; k; k--) {
                if (a[i - 1] == b[j - 1]) 
                    pre[j][k] = (pre[j - 1][k] + dp[j - 1][k - 1]) % MOD;
                else pre[j][k] = 0;

                dp[j][k] = (dp[j][k] + pre[j][k]) % MOD;
            }
        }
    }
    cout << dp[m][K] << endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    // init();
    // CaseT
    solve();
    return 0;
}

对局匹配

链接

https://www.lanqiao.cn/problems/107/learning/?page=1&first_category_id=1&sort=students_count&second_category_id=3&tags=%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92,%E5%9B%BD%E8%B5%9B

思路

进行分组dp,这里是差值只要是k就不能同时选,所以会将差值为k的划分为一组,然后每一组进行动态规划就可以了

dp[i]=max(dp[i−2]+arr[i], dp[i−1])

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int cnt[N], arr[N], dp[N];
int n, k;
 
int main() { 
    while (scanf("%d%d", &n, &k) == 2) {//输入个数n,差值k
        memset(cnt, 0, sizeof(cnt));//这里的cnt是记录每个值出现的次数
        int val, ans = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &val);
            cnt[val]++;
        }
        //特殊处理k=0的情况
        //处理思路是把所有的出现的元素都给ans加一
        if (k == 0) {
            for (int i = 0; i < N; i++) {
                if (cnt[i]) ans++;//计算有多少种数字
      
            }
        }
        else {
            for (int i = 0; i < k; i++) {//k个小组
                int len = 0;
                for (int j = i; j < N; j += k) {
                    arr[len++] = cnt[j];//遍历每个小组中的所有元素
                    //arr数组记录了每个元素的个数
                }
                //状态转移方程
                dp[0] = arr[0];
                for (int j = 1; j < len; j++) {
                    if (j == 1) 
                    {
                        dp[j] = max(dp[0], arr[j]);
                    }
                    else 
                    {
                        dp[j] = max(dp[j - 2] + arr[j], dp[j - 1]);
                    }
                }
                ans += dp[len - 1];//把k组的值都相加
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

货币系统

链接

https://www.luogu.com.cn/problem/P5020

思路

若两个货币系统等价,有如下性质

 1. a1,a2,...,an一定都可以被表示出来

 2. 在最优解中,b1,b2,...bm一定都是从a1,a2,...,an中选择的

 2. b1,b2,...,bm一定不能被其他bi表示出来

步骤
由于数据中不存在负数,将 a[] 数组从小到大排序

 1. 若ai能被a0~a(i-1)表示出来,则一定不选

 2. 若ai不能被能被a0~a(i-1)表示出来,则一定选

时间复杂度 O(nm)

代码

#include "bits/stdc++.h"
using namespace std;
const int maxn=25010;
typedef long long ll;
int t,n,f[maxn],a[110];

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        memset(f,0,sizeof f);
        f[0]=1;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        sort(a,a+n);
        int v=a[n-1],ans=0;
        for(int i=0;i<n;i++){
            if(f[a[i]]==0)  ans++;
            for(int j=a[i];j<=v;j++){
                f[j]+=f[j-a[i]];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

最优包含

链接

https://www.lanqiao.cn/problems/239/learning/?page=1&first_category_id=1&sort=students_count&second_category_id=3&tags=%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92,%E5%9B%BD%E8%B5%9B

思路

状态定义:f[i][j]表示第一个字符串前i个字符,第二个字符串前j个字符,第一个字符串前i个字符包含第二个字符串前j个字符的最少修改次数

初始状态:f[i][0] = 0 other f[i][j] = INF

状态转移:s1[i]==s2[j] f[i][j] = f[i - 1][j - 1]

                  s1[i] != s2[j] max(f[i - 1][j - 1] + 1, f[i - 1][j])

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1024, INF = 0x3f3f3f3f;
int f[N][N];
int main() 
{
  string s1, s2;
  cin >> s1 >> s2;
  int n = s1.size();
  int m = s2.size();
  memset(f, 0x3f, sizeof(f));
  for (int i = 0; i <= n; i++) f[i][0] = 0;
  for (int i = 1; i <= n; i++) 
  {
    for (int j = 1; j <= i && j <= m; j++) 
    {
      if (s1[i - 1] == s2[j - 1]) f[i][j] = f[i - 1][j - 1];
      else 
      {
        f[i][j] = f[i - 1][j - 1] + 1;
        if (i - 1 >= j) f[i][j] = min(f[i][j], f[i - 1][j]);
      }
    }
  }
  cout << f[n][m] << endl;
  return 0;
}

合根植物

链接https://www.lanqiao.cn/problems/110/learning/?page=1&first_category_id=1&sort=students_count&second_category_id=3&tags=%E5%B9%B6%E6%9F%A5%E9%9B%86

思路

编号为 a 的小格子和编号为 b 的小格子合根表示他们合并在了同一个集合里,把所有合并之后,若发现某一个格子的father等于格子编号,说明这是一个集合,统计一下就可以算出共有多少个合根植物。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 20;
int m, n, k, fa[N];

int find(int x){
  if(x == fa[x]) return x;
  return fa[x] = find(fa[x]);
}

void uni(int x, int y){
  int fx = find(x), fy = find(y);
  fa[fx] = fy;
}

int main()
{
  cin >> m >> n >> k;
  for(int i = 1; i <= m * n; i++) fa[i] = i;
  while(k--){
    int x, y;
    cin >> x >> y;
    uni(x, y);
  }
  int ans = 0;
  for(int i = 1; i <= m * n; i++){
    if(find(i) == i) ans++;
  }
  cout << ans;
  return 0;
}

Bitwise Exclusive-OR Sequence

链接

https://ac.nowcoder.com/acm/contest/24346/B?&headNav=acm

思路

种类并查集,首先可能是多个图, 对于每个图中的每一位, 取0 或者取1 都是可以确定图上的其他数字取0 或者取1 ,所以我们可以去枚举取0 或者取1 ,来取个min 得到结果。
所以这里我们用并查集, 最大数为 2的30次, 所以我们对于每一位都做一个并查集, 就是30个并查集。 f[a] 表示当前位置取1,f[a+n] 表示当前位取0,这样就可以枚举区间了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5+10;
ll n, m, fa[N], sz[N];
ll a[N];

struct Node
{
	ll a, b;
	ll val;
}num[N];

ll find(ll x)
{
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

void init()
{
	for(ll i = 1; i <= n; i ++)
	{
		fa[i] = i, sz[i] = 1;
		fa[i+n] = i+n, sz[i+n] = 0;
	}
}

void uni(ll a, ll b)
{
	sz[a] += sz[b];
	fa[b] = a;
}

int main()
{
	cin >> n >> m;
	for(int i = 1; i <= m; i ++)
	{
		scanf("%lld%lld%lld", &num[i].a, &num[i].b, &num[i].val);
	}
	
	ll res = 0;
	for(int i = 0; i < 30; i ++)
	{
		init(); // 每一次做并查集都要初始化!
		for(int j = 1; j <= m; j ++)
		{
			int a1 = find(num[j].a);
			int b1 = find(num[j].b);
			
			int a2 = find(num[j].a + n);
			int b2 = find(num[j].b + n);
			
			if((num[j].val >> i) & 1 ) // 不同为1 ,一个取0一个取1
			{
				if(a1 == b1)
				{
					cout << -1 << endl;
					return 0;
				}
				if(a1 == b2) continue;
				
				uni(a1, b2);
				uni(b1, a2);
			}
			else // 相同为0, 都取1 或者都取0
			{
				if(a1 == b2)
				{
					cout << -1 << endl;
					return 0;
				}
				if(a1 == b1) continue;
				
				uni(b1, a1);
				uni(b2, a2);
			}
		}
		for(int j = 1; j <= n; j ++) // 对于每个数字计算结果
		{
			res += (ll)min(sz[find(j)], sz[find(j+n)]) * (1 << i);
			sz[find(j)] = 0;
			sz[find(j+n)] = 0;
		}
	}
	
	cout << res << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值