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(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),放到优先队列里面,
- 每一次取最大的a[x]+b[y],
- 将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=();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
题解
- (x,y) ->(n-x+1, n-y+1)
- (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;
}