小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。
例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。
现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?
输入描述
一个仅包含’0’和’1’的字符串,长度不超过 2000。
输出描述
所有非空子串的权值和。
示例1
输入
10001
输出
8
说明
长度为 2 的子串中,有 2 个"00"的权值是 1。
长度为 3 的 3 个子串权值都是 1。
长度为 4 的 2 个子串权值都是 1。
长度为 5 的 1 个子串权值是 1。
总权值之和为 2+3+2+1=8
思路与代码
前缀和模拟。
不难发现,最后的序列必然是 10101…或者是 010101…,因此对于长度为n的序列,最后的结果只有2种可能。枚举其中的最小值即可。
pres1和pres2分别表示两种可能的修改次数的前缀和。
枚举所有可能的子串,使用前缀和快速求得区间的修改次数(权值),叠加即可。
s = [int(c) for c in input()]
n = len(s)
# 枚举第一位就好了
s1 = list(s)
s2 = list(s)
s2[0] ^= 1
for i in range(1,n):
s1[i] = s1[i-1] ^ 1
s2[i] = s2[i-1] ^ 1
pres1, pres2 = [0]*(n+1),[0]*(n+1)
for i in range(1, n+1):
pres1[i], pres2[i] = pres1[i-1], pres2[i-1]
//需要改动,则次数+1
if s[i-1] != s1[i-1]: pres1[i] += 1
if s[i-1] != s2[i-1]: pres2[i] += 1
res = 0
//将不同情况字串的结果相加
for i in range(n):
for j in range(i+1,n):
res += min(pres1[j+1]-pres1[i], pres2[j+1]-pres2[i])
print(res)
来自 https://mp.weixin.qq.com/s/E9vgnAASzcuojHq8DDGasw
有一个n行m列的棋盘,有一些格子是障碍物不能通过。小红控制一个皇后在从左上角出发,每次移动她可以控制皇后进行以下三种方式中的一种:
1.向右移动若干个格子
2.向下移动若干个格子
3.向右下移动若干个格子。
用数学语言描述,当前的坐标在(x,y)时,每次移动可以到(x+k,y)或(x,y+k)或(x+k,y+k)其中k为任意正整数。移动的前提是,路径上没有障碍物。
小红想知道,皇后从左上角移动到右下角,最少要移动多少步?
输入描述
第一行输入两个正整数n和n,代表行数和列数。
接下来的n行,每行输入一个长度m的字符串,用来表示棋盘。
其中’.‘代表可以通过的位置,'*'代表障碍物。
保证左上角和右下角都不是障碍物。
1<=n,m<=2000。
输出描述
如果无法到达,请输出-1。
否则输出一个整数,代表最少的移动次数。
思路与代码:
记录到达每个点的步数,向三个方向走的时候每次尽可能走到最远;在遍历三个方向的过程中,首先需要判断一下当前节点和对应方向的上一个节点是否步数相同,如果相同,则说明可以一步到达,则没必要再遍历这个方向上的点,跳过此方向;否则则说明改变了前进方向,在当前节点的步数基础上+1才可到达下一个节点。
#include
using namespace std;
int main(){
int n,m;
cin>>n>>m;
vector<vector<char>> grid(n,vector<char>(m));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>grid[i][j];
}
}
int dx[3]={1,0,1};
int dy[3]={0,1,1};
vector<vector<int>> distance(n,vector<int>(m,INT_MAX));
distance[0][0]=0;
queue<pair<int,int>> q;
q.push({0,0});
while(!q.empty()){
auto current = q.front();
q.pop();
int x = current.first;
int y = current.second;
for(int i=0;i<3;i++){
//三种方向的试探
int oldX = x-dx[i];
int oldY = y-dy[i];
if(oldX>=0&&oldX<n&&oldY>=0&&oldY<m){
if(distance[oldX][oldY]==distance[x][y]){
continue;
}
}
int index=1;
do{
int newX = x+dx[i]*index;
int newY = y+dy[i]*index;
index++;
if(newX<0||newY<0||newX>=n||newY>=m||grid[newX][newY]=='*'){
break;
}
if(distance[newX][newY]>distance[x][y]+1){
//如果原先这个格子已经走过了,那么就将他更新为最近的最小值
distance[newX][newY] = distance[x][y]+1;
q.push({newX,newY});
}
}while(true);
}
}
if(distance[n-1][m-1]==INT_MAX){
cout<<-1<<endl;
}
else{
cout<<distance[n-1][m-1]<<endl;
}
return 0;
}
参考京东秋招0819-https://ujimatsu-chiya.github.io
小红准备买药治病。已知共有n种症状和m种药,第i种药可以治疗一些症状,但可能会导致一些副作用,添加一些新的症状。小红依次服用了一些药,请你告诉小红,当她每次服用一副药时,当前还有多少症状?
输入描述
第一行输入一个正整数n,代表症状的数量
第二行输入一个长应为n的01串,第i位是‘1’代表小红目前有第i个症状,第i位是‘0’代表没有该症状。
第三行输入一个正整数m,代表药的数量
接下来的2 * m行,每2行描述一副药:
第一行输入一个长度为n的01串,代表该药能治疗的症状。’1‘代表可以治疗,‘0’代表不能治疗。
第二行输入一个长度为n的01串,代表该药会产生的副作用。’1‘代表会产生该症状,’0‘代表不会产生。
接下来的一行,输入一个正整数q,代表小红服用的药数量。
接下来的q行,每行输入一个正整数u,代表小红服用了第u副药。
1<=n<=20
1<=m,q<=10^4
1<=ai,u<=m
保证每副药的副作用产生的症状和该药治疗的症状是不会重复的,即不会存在同一个位置的两个01串都是‘1’。
输出描述
输出q行,每行输入一个正整数,代表当前小红服用药后,身体有多少症状。
示例1
4
0101
3
1100
0010
0101
1000
1001
0000
3
2
3
1
输出
1
0
1
# include
using namespace std;
const int N=10004;
int m1[N],m2[N],n,q,m;
int parse(string &s){
int x=0;
for(int i=0;i<n;i++)
x|=int(s[i]-'0')<<i;
return x;
}
int main(){
string s,t;
cin>>n>>s>>m;
int st=parse(s);
for(int i=1;i<=m;i++){
cin>>s>>t;
m1[i]=parse(s);
m2[i]=parse(t);
}
cin>>q;
int id;
while(q--){
cin>>id;
//((1<
//m1[i]是第i种药的治疗效果,与全1序列异或的结果是取反。最终将关键位置原本为1,先取反就变为了0,但非关键位全为1
//将((1<
st &= ((1<<n)-1)^m1[id];
//st |= m2[id];就是添加副作用病症
st |= m2[id];
cout<<__builtin_popcount(st)<<"\n";
}
}
来自:https://mp.weixin.qq.com/s/5-A_-HcahKScXnMopcVKyg
题目描述:
定义f(A)表示将序列A进行unique操作之后的序列的元素个数。unique 操作是指将相邻且相同的元素合成一个元素,再按照原序列的相对顺序进行排列之后得到的序列。例如,[1,1,1,2,2,3,3,1] 进行 unique 操作之后的序列为[1,2,3,1]; [1,2,3,3,2,1] 进行unique操作之后的序列为[1,2,3,2,1]; [1,1,1,1,1] 进行unique操作之后得到的序列为 [1]。
现在,输入一个长度为n的序列S,你需要将其划分为k段,使得每一段都不为空,且你需要最大化所有段的f函数的值之和。你只需要输出这个最大值就行。
输入描述
第一行两个正整数n,k(1<=k<=n<=10^5);
第二行n个由空格隔开的正整数a1,a2,…,an(1<=ai<=10000),表示输入的序列S.
输出描述
输出一个整数,表示所求的最大值。
样例输入
8 3
1 1 1 2 2 3 3 1
样例输出
6
思路与代码
观察得出以下规律:
因此,可以先算出在不切割的时候的f函数的值之和 res,最多可以切割k-1次,也就是最多可以使得res增大k-1次,只要有k-1个切割点满足相邻且不同。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
/**
* 1 1 1 2 2 3 3 1
*
* */
int[] a = new int[n];
int samecnt = 0;
int res = 0;
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
if (i > 0 && a[i] == a[i-1]) samecnt++;
if (i == 0 || a[i] != a[i-1]) res++;
}
//两种情况,取较小值
//可以来切分的相同序列长度 为较小值,就把他全切完
//可以来切分的相同序列长度 为较大值,那就把k-1次切割机会用完
System.out.println(res + Math.min(samecnt, k-1));
}
}