本次双周赛重要知识点为前缀和,之前不了解前缀和相关知识点,导致题目做的磕磕绊绊
连续子数组问题往往可以通过前缀和来解决
class Solution {
public int sumOddLengthSubarrays(int[] arr) {
int len = arr.length;
int res = 0;
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
if((j-i)%2==0){
res+= add(arr,i,j);
}
}
}
return res;
}
public int add(int[] arr, int start, int end){
int res = 0;
for(int i=start; i<=end; i++){
res += arr[i];
}
return res;
}
}
// 前缀和
// 在解法一中,add函数是计算数组从start到end的和,数组被反复计算多次,这里可以使用前缀和预先计算
class Solution{
public int sumOddLengthSubarrays(int[] arr){
int len = arr.length;
int res = 0;
int[] prefix = new int[len];
prefix[0] = arr[0];
for(int i=1; i<len; i++){
prefix[i] = prefix[i-1] + arr[i];
}
for(int i=0;i<len;i++){
for(int j=i;j<len;j+=2){
if(i==0)
res+= prefix[j];
else
res+= prefix[j]-prefix[i-1];
}
}
return res;
}
}
class Solution {
// 子数组连续
// 只能删除一个子数组
// 回溯,每次只删除一个元素,下一步删除下一个元素
// 当删除结果可以被p整除时将该结果更新到minDelete中
// 回溯完全部分支后能得到最小删除数组
int minDel;
int p;
public int minSubarray(int[] nums, int p) {
long numsSum = 0;
minDel = Integer.MAX_VALUE;
this.p = p;
for(int i=0;i<nums.length;i++){
numsSum += nums[i];
}
//不需要删除任何元素,和已经能被p整除
if(numsSum%p==0)return 0;
for(int i=0; i<nums.length;i++){
deleteDFS(nums, numsSum-nums[i], i,i);
}
//当minDel为Integer.MAX_VALUE时,回溯中没有进行任何一次删除,即任何方案都不能使得和被整除
return minDel==Integer.MAX_VALUE?-1:minDel;
}
public void deleteDFS(int[] nums, long left, int delBegin, int cur){
if(left<p)return;
//当前删除已可以被整除时,不需要判断下一个,下一个minDel一定比当前minDel大
if(left%p==0){
minDel = Math.min(minDel, cur-delBegin+1);
return;
}
if(cur+1<nums.length)
deleteDFS(nums, left-nums[cur+1], delBegin, cur+1);
}
}
class Solution{
// 取模前缀和
public int minSubarray(int[] nums, int p){
long sum =0;
for(int num:nums){
sum+=num;
}
int mod = (int)(sum%p);
System.out.println(mod);
if(mod==0)return 0;
int res = nums.length;
// key-value : curmod-index
HashMap<Integer,Integer> map = new HashMap<>();
// 当curmod==mod的时候,该前缀和求模等于总和求模,所以该前缀全部可以删除
// 此时的target==0
map.put(0,-1);
sum=0;
for(int i=0; i<nums.length;i++){
sum += nums[i];
int curmod = (int)(sum%p);
int targetmod = curmod>=mod? curmod-mod: curmod-mod+p;
if(map.containsKey(targetmod)){
res = Math.min(res, i-map.get(targetmod));
}
map.put(curmod, i);
}
return res==nums.length?-1:res;
}
}
class Solution {
// 使用requests计算每个位置被查询的次数,查询次数最多的位置放置数组最大值,第二多的放数组第二大值
public int maxSumRangeQuery(int[] nums, int[][] requests) {
int res = 0;
int[] times = new int[nums.length];
HashMap<Integer,Integer> start = new HashMap<>();
HashMap<Integer,Integer> end = new HashMap<>();
for(int[] request:requests){
start.put(request[0], start.getOrDefault(request[0],0)+1);
end.put(request[1], end.getOrDefault(request[1],0)+1);
}
// System.out.println(end);
int count=0;
for(int i=0;i<nums.length; i++){
if(start.containsKey(i)){
count+=start.get(i);
}
times[i] = count;
if(end.containsKey(i)){
count-=end.get(i);
}
}
// System.out.println(Arrays.toString(times));
Arrays.sort(times);
Arrays.sort(nums);
for(int i=0; i<nums.length; i++){
// if(nums[i]==0)break;
res += times[i]*nums[i];
res %= 1000000007;
}
return res;
}
}
class Solution {
// 使用requests计算每个位置被查询的次数,查询次数最多的位置放置数组最大值,第二多的放数组第二大值
public int maxSumRangeQuery(int[] nums, int[][] requests) {
int res = 0;
int[] fre = new int[nums.length+1];
for(int[] request:requests){
fre[request[0]] ++;
fre[request[1]+1] --;
}
for(int i=1; i<nums.length; i++){
fre[i] += fre[i-1];
}
//System.out.println(Arrays.toString(fre));
Arrays.sort(fre);
Arrays.sort(nums);
for(int i=nums.length; i>=1 && fre[i]>0; i--){
// if(nums[i]==0)break;
res += fre[i]*nums[i-1];
res %= 1000000007;
}
return res;
}
}
class Solution {
// 建立有向图,对每个颜色建立边
// 图必须存在拓扑排序,即一个颜色必须在另一个颜色之后进行染色
public boolean isPrintable(int[][] targetGrid) {
int rows= targetGrid.length, cols = targetGrid[0].length;
// 初始化每个颜色矩阵的边界
int[] top= new int[61],bottom= new int[61],left= new int[61],right = new int[61];
Arrays.fill(top,61);
Arrays.fill(left,61);
Arrays.fill(bottom,-1);
Arrays.fill(right,-1);
int color = 0;
// 建立每个颜色的矩阵边界
for(int i=0;i<rows;i++){
for(int j=0; j<cols; j++){
color = targetGrid[i][j];
top[color] = Math.min(top[color], i);
bottom[color] = Math.max(bottom[color], i);
left[color] = Math.min(left[color], j);
right[color] = Math.max(right[color], j);
}
}
// 根据矩阵建立有向图,遍历targetGrid,
// 当前位置颜色X在某个矩阵A中但是不为矩阵A的颜色时,建立从A到X的边
// X可以存在于多个矩阵中
// 变量:是否存在边-防止重复建立边;入度,便于后期判断是否拓扑排序;邻接表,从i出发到达的点
boolean[][] hasEdge= new boolean[61][61];
int[] inDegree = new int[61];
List<List<Integer>> adjs = new ArrayList<>();
for(int i=0;i<=60;i++){
adjs.add(new ArrayList<Integer>());
}
int curColor = 0;
// 建立图
for(int i=0;i<rows;i++){
for(int j=0; j<cols; j++){
curColor = targetGrid[i][j];
for(color=1; color<=60; color++){
if(i>=top[color] && i<=bottom[color] && j>=left[color] && j<=right[color])
if(curColor!=color && !hasEdge[color][curColor]){
adjs.get(color).add(curColor);
inDegree[curColor]++;
hasEdge[color][curColor] = true;
}
}
}
}
// 寻找入度为0的颜色点,减小该点连结的点的入度,直到所有点的入度都为0
ArrayList<Integer> zeroC = new ArrayList<>();
while(true){
int i;
for(i=1;i<=60;i++){
if(inDegree[i]==0){
zeroC.add(i);
for(Integer pointTo:adjs.get(i)){
inDegree[pointTo]--;
}
inDegree[i]=-1;
break;
}
}
if(i==61)break;
}
return zeroC.size()==60;
}
}
原文:https://www.cnblogs.com/Yuasin/p/13703988.html