1、输入一串数 等于某值的数的个数——hashmap的使用
我的做法,先读入这一串数,遍历一遍找出等于某个值的数的个数,超时(空间复杂度低但时间复杂度高)
正确做法,在读入这一串数的同时创建一个hanshmap key为值、value为个数,直接找出等于某个值的数的个数
import
java.util.*;
public
class
Main{
public
static
void
main(String[]args){
Scanner in=
new
Scanner(System.in);
while
(in.hasNext()) {
LinkedHashMap<Integer,ArrayList<Integer>>map=
new
LinkedHashMap<>();
int
n=in.nextInt();
int
[]a=
new
int
[n];
for
(
int
i=
0
;i<n;i++) {
a[i]=in.nextInt();
if
(map.containsKey(a[i])) {
map.get(a[i]).add(i+
1
);
}
else
{
ArrayList<Integer>list=
new
ArrayList<Integer>();
list.add(i+
1
);
map.put(a[i], list);
}
}
int
q=in.nextInt();
for
(
int
i=
0
;i<q;i++) {
int
l=in.nextInt();
int
r=in.nextInt();
int
k=in.nextInt();
int
count=
0
;
ArrayList<Integer>list=map.get(k);
if
(list!=
null
&&list.size()!=
0
) {
for
(Integer num:list) {
if
(num>=l&&num<=r)
count++;
}
}
System.out.println(count);
}
}
}
}
2、字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?——区间动态规划
要求移动后形成的最长连续子串,这个最长连续子串可能全是a
或b
……c
。因此,这里需要枚举移动后形成的最长连续子串里面所包含的字母;
确定了里面包含的字母,就可以专注于这个字母了,也就是说其余的字母都是没有用的,把它们从序列中挖掉;然后就值剩下目标字母了,目标字母离散地分布在序列中,因此,再离散化一下,搞完之后会生成一个行的序列,之后的动态规划就在新的序列上进行。
下面的图片表达了上述过程:
现在新的序列pos
看起来是合在了一起,形成了最长连续子序列,但是,形成这些连续序列所需要的操作次数是多少呢?如果操作次数大于m
,那么该序列就是不满足要求的;
因此,这里面就可以得出区间动态规划了,先从小到大枚举段长,依次求得该段长的所有子序列的操作次数,并判断是否小于等于m
,如果满足要求,就更新答案。
从小到大枚举段长是为了利用子问题的结果;dp[i][j]
表示把pos[i]
和pos[j]
之间的目标字母移动到一起,形成j - i + 1
长度的连续子序列所需要的操作次数;
状态转移方程:dp[i][i + len - 1] = dp[i + 1][i + len - 2] + pos[i + len - 1] - pos[i] - len + 1;
,依据是|x−a|+|x−b||x−a|+|x−b|在什么时候取得最小值。用最小的移动次数把两个目标字母移动到一起的方法就是把两个目标字母都往中间靠,状态转移方程就是根据这个来的,先把pos[i + 1] ~ pos[i + len - 2]
之间的目标字母移动到一起,这个移动次数就是dp[i + 1][i + len - 2]
,然后把两个端点pos[i]
和pos[i + len -1]
处的目标字母往中间靠,所需要的移动次数是pos[i + len - 1] - pos[i] - len + 1
。
3、
/**
* 有n个房间,现在i号房间里的人需要被重新分配,分配的规则是这样的:先让i号房间里的人全都出来,接下来按照 i+1, i+2, i+3, ...
* 的顺序依此往这些房间里放一个人,n号房间的的下一个房间是1号房间,直到所有的人都被重新分配。
*
* 现在告诉你分配完后每个房间的人数以及最后一个人被分配的房间号x,你需要求出分配前每个房间的人数。数据保证一定有解,若有多解输出任意一个解。
*
*
* 输入描述: 第一行两个整数n, x (2<=n<=10^5, 1<=x<=n),代表房间房间数量以及最后一个人被分配的房间号; 第二行n个整数
* a_i(0<=a_i<=10^9) ,代表每个房间分配后的人数。
*
*
* 输出描述: 输出n个整数,代表每个房间分配前的人数。
*
* 输入例子1: 3 1 6 5 1
*
* 输出例子1: 4 4 4
*
* @param args
*/
首先明确一点,最初被重新分配的房间一定是人数最少的房间(或之一)。因为里面的人都出来后人数为0,而下一次加1要等到所有其他房间都加1之后。
读入的同时找出人数最少的房间的人数(因为人数最少的房间有多个,还不能通过这一步就确定最初被分配的房间是哪个)。
每个房间同时减去该人数。
然后设置一个游标,从最后一个人被分配的房间开始,每次给当前房间-1,然后给一个临时变量temp+1,然后游标向左移动,直到找到第一个人数为0的房间,最初被分配的房间就是它了!
然后将临时变量temp加到最初房间的人数中,再将刚才减去的最少的房间的人数加回来。
import
java.util.*;
public
class
Main{
public
static
void
main(String[]args){
Scanner in=
new
Scanner(System.in);
while
(in.hasNext()) {
int
n=in.nextInt();
int
x=in.nextInt();
long
[]a=
new
long
[n];
long
sum=
0
;
long
minnum=Integer.MAX_VALUE;
for
(
int
i=
0
;i<n;i++) {
a[i]=in.nextInt();
if
(minnum>a[i]) {
minnum=a[i];
}
}
for
(
int
i=
0
;i<n;i++) {
a[i]-=minnum;
sum+=minnum;
}
int
pointer=x-
1
;
while
(
true
) {
if
(a[pointer]==
0
)
break
;
a[pointer]--;
sum++;
if
(pointer==
0
)
pointer=n-
1
;
else
pointer--;
}
a[pointer]=sum;
for
(
int
i=
0
;i<n;i++) {
if
(i==n-
1
)
System.out.println(a[i]);
else
System.out.print(a[i]+
" "
);
}
}
}
}