牛客小白月赛34

dd爱科学1.0

链接:https://ac.nowcoder.com/acm/contest/11211/A
来源:牛客网

题目描述
大科学家dddd最近在研究转基因白菜,白菜的基因序列由一串大写英文字母构成,dddd经过严谨的推理证明发现,只有当白菜的基因序列呈按位非递减形式时,这株白菜的高附加值将达到最高,于是优秀的dddd开始着手修改白菜的基因序列,dddd每次修改基因序列的任意位需要的代价是1,dddd想知道,修改白菜的基因序列使其高附加值达到最高,所需要的最小代价的是多少。
输入描述:
第一行一个正整数n(1≤n≤1000000)
第二行一个长度为n的字符串,表示所给白菜的基因序列
保证给出字符串中有且仅有大写英文字母
输出描述:
输出一行,表示最小代价
示例1
输入
复制
5
ACEBF
输出
复制
1
说明
改成ACEEF或者ACEFF,都只用改动一个字符,所需代价最小为1。

题解

n - 最长不下降子序列长度
二分做法

#include<bits/stdc++.h>
using namespace std;
const int  N = 1000000;
int a[N], q[N];
int main(){
    int n;
    string s;
    cin>>n>>s;
    for(int i = 0; i < n; i++){
        a[i] = s[i] - 'A';
    }
    int len = 0;
    for (int i = 0; i < n; i ++ )
    {
        int l = 0, r = len;
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (q[mid] <= a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1);
        q[r + 1] = a[i];
    }

    printf("%d\n",n - len);

    return 0;
}

另一种解法
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int n,ans;
char a[1000005];
int f[1000005][30];
int main(){
	scanf("%d",&n);
	scanf("%s",a+1);
	memset(f,0x3f,sizeof(f));
	for (int i=1;i<=26;i++)f[0][i]=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=26;j++)f[i][j]=min(f[i][j-1],f[i-1][j]+(a[i]!=j+'A'-1));
	printf("%d",f[n][26]);
} 

dd爱科学2.0

链接:https://ac.nowcoder.com/acm/contest/11211/C
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
大科学家dddd最近在研究转基因白菜,白菜的基因序列由一串大写英文字母构成,dddd经过严谨的推理证明发现,只有当白菜的基因序列呈按位非递减形式时,这株白菜的高附加值将达到最高,于是优秀的dddd开始着手修改白菜的基因序列,dddd每次可以选择基因序列的一位进行修改,每次当她把一个字母xx修改成yy时,会产生|x-y|∣x−y∣(即xx与yy的ASCIIASCII码差值的绝对值)的改动偏移量,dddd希望,修改白菜的基因序列使其高附加值达到最高,并且基因序列的改动偏移量总和最小,她想知道最小的改动偏移量总和是多少。
输入描述:
第一行一个正整数n(1≤n≤1000000)
第二行一个长度为n的字符串,表示所给白菜的基因序列
保证给出字符串中有且仅有大写英文字母
输出描述:
输出一行,表示最小改动偏移量总和
示例1
输入
复制
5
AEEBC
输出
复制
5
说明
改成AEEEE或ACCCC偏移量总和最小
改成AEEEE,偏移量总和为|B-E|+|C-E|=3+2=5
改成ACCCC,偏移量总和为|E-C|+|E-C|+|B-C|=2+2+1=5
所以最小偏移量总和为5

题解

先转为为n*26的矩阵b[i][j],b[i][j]的意思为第i个字母转为第j个字母要画的步骤,即abs(j-a[i])
A: 0 1 2 3 4 5 6 … 25
E: 4 3 2 1 0 1 2 … 21
E: 4 3 2 1 0 1 2 … 21
B: 1 0 1 2 3 4 5 … 24
C: 2 1 0 1 2 3 4 … 23
相当于转化问题为求一个矩阵中只能列增加的最短路径的问题
答案如加粗数组。
设dp[i][j]为前i个字母最后一个字母变为j的最小路径。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
typedef long long LL;
int dp[maxn][26], a[maxn], b[maxn][26];
 
int main()
{
    for(int i = 0; i < maxn; i++){
        for(int j = 0; j < 26; j++){
            dp[i][j] = 1e9;
        }
    }
    int n;
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        char ch;
        cin>>ch;
        a[i]=ch-'A';
        for(int j = 0; j < 26; j++){
            b[i][j] = abs(a[i] - j);
        }
    }
     for(int j = 0; j < 26; j++){
         dp[1][j] = b[1][j];
     }
     
    for(int i = 2; i <= n; i++){
         
        for(int j = 0; j < 26; j++){
            for(int k = 0; k <= j; k ++){
                 dp[i][j] = min(dp[i][j], dp[i - 1][k] + b[i][j]);
            }
            
        }
    }
  
    int res = 1e9;
    for(int j = 0; j < 26; j++){
        res = min(res, dp[n][j]);
         //cout<<dp[n][j]<<endl;
     }
    cout<<res<<endl;
}

第二种解法:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int n,ans;
char a[1000005];
int f[1000005][30];
int main(){
	scanf("%d",&n);
	scanf("%s",a+1);
	memset(f,0x3f,sizeof(f));
	for (int i=1;i<=26;++i)f[0][i]=0;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=26;++j)f[i][j]=min(f[i][j-1],f[i-1][j]+abs(a[i]-(j-1+'A')));
	printf("%d",f[n][26]);
} 

dd爱探险

链接:https://ac.nowcoder.com/acm/contest/11211/B
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
星际中有nn个空间站,任意两个空间站间可以相互跳跃,由空间站x跳跃到空间站y所需要的代价为 P[x][y],注意不保证 p[x][y]= p[y][x],dddd可以任意选择出发的空间站,并通过恰好n−1次跳跃把所有空间站跳完,并且dddd必须选择22次跳跃,其中一次跳跃中进行重力加速,另一次跳跃中进行反重力加速,重力加速会导致当前跳跃代价变为0,反重力加速会导致当前跳跃代价翻倍(乘2),问跳完所有空间站所需要最小代价
输入描述:
第一行一个数n(3≤n≤16)
接下来n行每行n个数,第x行第y个数表示p[x][y] (0≤p[x][y]≤100000)
输出描述:
一个数,表示最小代价
示例1
输入
复制
3
0 2 1
2 0 1
3 1 0
输出
复制
2
说明
1->2重力加速
2->3反重力加速
代价0+1*2=2

题解

状压DP
最短Hamilton路径的扩展
不同点:最短Hamilton路径需要从0->n-1,固定了终点和起点。
这题没有,所有做的时候需要注意初始化和最后的答案获取。

状态表示
f[i][j][k]所有从0走到j,走过所有点的二进制表示为i,状态为k的所有路径的最小值
状态计算
依据倒数第二个走到哪一个点k来划分
0->k->j
f[i][j] = min(f[i][j],f[ i - {j} ][k]+a[k][j])

  • k=0表示没经过加速
  • k=1表示经过一次重力加速,
  • k=2表示经过一次反重力加速
  • k=3表示两次加速都已经结束
    重新加一下状态转移,如代码所示
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 16;
int f[1<<N][N][4],a[N][N];

int main(){
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            scanf("%d", &a[i][j]);
        }
    }

    memset(f, 0x3f, sizeof(f));
    for(int i = 0; i < n; i++){
        f[1<<i][i][0] = 0;
    }
    

    for(int i = 0; i < (1 << n); i++){
        for(int j = 0; j < n; j++){
            if(i >> j & 1){
                for(int k = 0; k < n; k++){
                    if(i >> k & 1){// 正确理解应为(i - (1 << j)) >> k & 1

                        f[i][j][0] = min(f[i][j][0], f[i - (1 << j)][k][0] + a[k][j]);
                        f[i][j][1] = min(f[i][j][1], f[i - (1 << j)][k][0]);
                        f[i][j][1] = min(f[i][j][1], f[i - (1 << j)][k][1] + a[k][j]);
                        f[i][j][2] = min(f[i][j][2], f[i - (1 << j)][k][0] + 2 * a[k][j]);
                        f[i][j][2] = min(f[i][j][2], f[i - (1 << j)][k][2] + a[k][j]);

                        f[i][j][3] = min(f[i][j][3], f[i - (1 << j)][k][1] + 2 * a[k][j]);
                        f[i][j][3] = min(f[i][j][3], f[i - (1 << j)][k][2]);
                        f[i][j][3] = min(f[i][j][3], f[i - (1 << j)][k][3] + a[k][j]);
                    }
                }
            }
        }
    }
    int res = 1e9;
    for(int i = 0; i < n; i++){
        //cout<<f[(1 << n) - 1][i][3]<<endl;
        res = min(res, f[(1 << n) - 1][i][3]);
    }
    printf("%d\n", res);
    return 0;
}

dd爱矩阵

链接:https://ac.nowcoder.com/acm/contest/11211/D
来源:牛客网
题目描述
给一个n*n的矩阵,每行各选一个数字,把选出来的n个数字相加,一共有 n n n^{n} nn种可行答案,你只要输出前n大就行了。

输入描述:
第一行一个数正整数n(1≤n≤500),表示矩阵大小
接下来n行,每行n个数描述矩阵,矩阵中数字取值范围在[1,500000]内
输出描述:
一行,n个数,输出前n大的结果
示例1
输入
复制
5
6 6 4 4 5
5 6 8 5 7
6 1 6 9 4
4 8 8 1 5
9 2 3 9 3
输出
复制
40 40 40 40 40

题解

在这里插入图片描述
首先考虑两行的情况
把两行分别降序 sort,如果令第一行为数组 a,第二行为数组 b

最大:a[1]+b[1]
第二大:a[1]+b[2] 或者 a[2]+b[1]
第三大:第二大淘汰的那一个。
第三大: a[1] + b[3] 或者 a[3] + b[1] 或者a[2] +b[2]

所以,我们首先可以把a[1]+b[i] (i属于1-n),放到优先队列里面,

  1. 每一次取最大的a[x]+b[y],
  2. 将a[x + 1]+b[y]放入优先队列

重复上述步骤

#include<bits/stdc++.h>
using namespace std;
int n,a[1005],b[1005],c[1005];
struct t{
	int x,z;
	friend bool operator<(t x,t y){
		return x.z<y.z;
	} 
};

bool cmp(int x,int y){
	return x>y;
}
int main(){
	priority_queue<t>q;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)scanf("%d",&b[i]);
	sort(b+1,b+1+n,cmp);
	for (int i=2;i<=n;i++){
		for (int j=1;j<=n;j++)scanf("%d",&a[j]);
		sort(a+1,a+1+n,cmp);
		for (int j=1;j<=n;j++){
			t p;
			p.x=1;p.z=a[1]+b[j];q.push(p);
            cout<<p.z<<endl;
		}
		for (int j=1;j<=n;j++){
			t pp=q.top();t p;
            cout<<i<<" "<<pp.x<<" "<<pp.z<<endl;
			q.pop();
			c[j]=pp.z;
			p.x=pp.x+1;
			p.z=pp.z-a[pp.x]+a[p.x];
			q.push(p);
		}
		for (int j=1;j<=n;j++)b[j]=c[j];
		while (!q.empty())q.pop();	
	}
	for (int j=1;j<=n;j++)printf(j==n?"%d\n":"%d ",b[j]);
}

dd爱旋转

在这里插入图片描述

链接:https://ac.nowcoder.com/acm/contest/11211/E
来源:牛客网

示例1
输入
复制
2
1 2
3 4
1
1
输出
复制
4 3
2 1
示例2
输入
复制
2
1 2
3 4
1
2
输出
复制
3 4
1 2

题解

  1. (x,y) ->(n-x+1, n-y+1)
  2. (x,y) ->(n-x+1, y)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int  N = 1010;
int n, a[N][N],res[N][N];
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            scanf("%d", &a[i][j]);
        }
    }
    int q;
    scanf("%d", &q);
    int dx = 0,dy = 0;
    while(q--){
        int x;
        scanf("%d", &x);
        if(x == 1) {
            dx++;
            dy++;
        }else{
            dx++;
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n ;j++){
             
            if(dx & 1 && dy & 1){
                //cout<<dx<<" "<<dy<<endl;
                res[n - i + 1][n - j + 1] = a[i][j];
            }
            else if(dx & 1 && !(dy & 1)){
                res[n - i + 1][j] = a[i][j];
            }
            else if(!(dx & 1) && dy & 1){
                res[i][n - j + 1] = a[i][j];
            }else if(!(dx & 1) && !(dy & 1)){
                res[i][j] = a[i][j];
            }
        }
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n ;j++){
            printf("%d ", res[i][j]);
        }
        printf("\n");
    }
    return 0;
}

dd爱框框

在这里插入图片描述

题解

双指针

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int  N = 10000010;
LL a[N], q[N];
int main(){
    LL n, x;
    scanf("%lld%lld", &n, &x);
    for(int i = 0; i < n; i++){
        scanf("%lld", &a[i]);
    }
    LL sum = 0, res = 1e9,resl = -1, resr = -1;
    for(int i = 0, j = 0; i < n; i++){
        while(j < n && sum < x){
            sum += a[j];
            j ++;
            //cout<<sum<<" "<<j<<endl;
        
        }
        //cout<<sum<<" "<<i<<" "<<j<<endl;
        if(sum >= x && res > j - i + 1){
            res = j - i + 1;
            resl = i + 1;
            resr = j ;
        }
         
        sum -= a[i];
    }
    printf("%lld %lld",resl, resr);
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值