【十五届蓝桥杯备赛】几个月没打代码的康复训练集

目录

P1 - 递归

92. 递归实现指数型枚举 - dfs枚举位置

94. 递归实现排列型枚举 - dfs枚举位置和方案

717. 简单斐波那契 - dfs / 滚动数组

P2 - 二分

★二分模板

503. 借教室 - 二分+差分

1227. 分巧克力 - 二分

5407. 管道 - 二分+区间合并

4956. 冶炼金属 - 二分最大最小值

789. 数的范围 - 二分最大最小值

1236. 递增三元组 - 二分

P3 - 前缀和

★一维前缀和模板

★二维前缀和模板 

1230. K倍区间 - 一维前缀和+数学

 4405. 统计子矩阵 - 二维前缀和

99. 激光炸弹 - 二维前缀和

P4 - 差分

★一维差分模板

★二维差分模板

5396.棋盘 - 二维差分

 4655. 重新排序 - 差分 + 排序

P5 - 双指针

799. 最长连续不重复子序列 - 滑动窗口

800. 数组元素的目标和 - 二分 / 双指针

2816. 判断子序列 - 双指针

1238. 日志统计 - 滑动窗口

P6 - BFS/DFS

1233. 全球变暖 - BFS

P7 - 区间合并

★区间合并模板

803. 区间合并

422. 校门外的树 -  模拟+区间合并

P8 - 日期问题

2867. 回文日期 - 回文串+日期模拟


基础算法模板传送门:【基础算法模板梳理】再也不想学算法了!(待更新)-CSDN博客

P1 - 递归

92. 递归实现指数型枚举 - dfs枚举位置

92. 递归实现指数型枚举 - AcWing题库

思路:

        题目要求枚举所有数的情况,则可以通过st[]数组标记已选位置,dfs从1开始枚举每一个位置u,一条路选该位置u的数字,另一条路不选该位置u的数字,当每一次枚举完所有位置(u>n)时,输出标记情况。

import java.util.*;

public class Main
{
    static int n;
    static boolean[] st=new boolean[20];
    
    public static void dfs(int u)
    {
        if(u>n) //所有位置枚举完则输出该条路径的选数方案
        {
            for(int i=1;i<=n;i++) 
                if(st[i]) System.out.print(i+" ");
            System.out.println();
            return;
        }
        st[u]=true; //左分支选该位置上的数
        dfs(u+1);
        st[u]=false; //右分支不选该位置上的数
        dfs(u+1);
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        dfs(1); //从位置1开始枚举
    }
}

94. 递归实现排列型枚举 - dfs枚举位置和方案

94. 递归实现排列型枚举 - AcWing题库

思路:

        题目要求枚举所有排列顺序,则dfs从1开始枚举每一个位置u,建立st[]数组记录每个数字的选择情况,用res[]记录选择方案。每一层递归枚举位置u,遍历所有数字 ,若该数字未被选择,则标记后放入res[u],进入下一层递归,枚举完一种情况后回溯,取消标记。

import java.util.*;

public class Main
{
    static int n;
    static boolean[] st=new boolean[10];
    static int[] res=new int[10];  //用于保存方案
    
    public static void dfs(int u)
    {
        if(u>n)
        {
            for(int i=1;i<=n;i++)
                System.out.print(res[i]+" ");
            System.out.println();
            return;
        }
        for(int i=1;i<=n;i++) //枚举每一个数字
            if(st[i]==false)
            {
                res[u]=i;  //如果该数字未被标记,则放在当前位置u上
                st[i]=true;
                dfs(u+1);
                st[i]=false;
            }
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        dfs(1);
    }
}

717. 简单斐波那契 - dfs / 滚动数组

717. 简单斐波那契 - AcWing题库

import java.util.*;

public class Main
{
    static int n;
    
    public static int dfs(int n)
    {
        if(n==0||n==1) return n;
        else return dfs(n-1) + dfs(n-2);
    }
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        long x;
        for(int i=0;i<n;i++)
        {
            x=dfs(i);
            System.out.print(x+" ");
        }
            
    }
}
import java.util.*;

public class Main
{
    static int n;
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        long a=0,b=1,c;
        for(int i=0;i<n;i++)
        {
            System.out.print(a+" ");
            c=a+b;
            a=b;
            b=c;
        }
    }
}

P2 - 二分

二分模板

  • l+r >> 1 ——  先r=mid —— 找左边界 —— 找大于等于某值的最小值
  • l+r+1 >>1 —— 先l=mid —— 找右边界 —— 找小于等于某值的最大值

503. 借教室 - 二分+差分

503. 借教室 - AcWing题库

思路:

        按先后顺序借教室,则容易想到每个人在【每天已有空教室数】中减去【需要的教室数】,则可以用差分模板。

        因为若某人的【所需空教室数】无法满足,则后面人的要求均无法满足,具有单调性,可以对每个人进行二分,check(mid)函数负责检验第mid人的需求是否被满足。

import java.util.*;

public class Main
{
    static int n,m;
    static int N=(int)1e6+10;
    static int r[]=new int[N];  //每天可提供空教室数
    static int d[]=new int[N];  //每个人每天所需教室数
    static int s[]=new int[N];  //每个人租赁开始时间
    static int t[]=new int[N];  //每个人租赁结束时间
    static long b[]=new long[N];   //差分数组
    
    public static boolean check(int mid) //如果第mid人不满足,则返回true
    {
        for(int i=1;i<=n;i++) b[i]=r[i]-r[i-1]; //构造差分数组
        
        for(int i=1;i<=mid;i++) //差分 对空教室数按第1-mid人的需求进行减少
        {
            b[s[i]]-=d[i];
            b[t[i]+1]+=d[i];
        }
        
        for(int i=1;i<=n;i++)
        {
            b[i]+=b[i-1];
            if(b[i]<0) return true;
        }
        return false;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        for(int i=1;i<=n;i++) r[i]=sc.nextInt();
        for(int i=1;i<=m;i++) 
        {
            d[i]=sc.nextInt();
            s[i]=sc.nextInt();
            t[i]=sc.nextInt();
        }
        
        if(!check(m)) //如果最后一个人都满足,则所有订单均可满足
        {
            System.out.print(0);
            return;
        }
        
        int l=1,r=m;
        while(l<r)
        {
            int mid=l+r>>1;
            if(check(mid)) r=mid;
            else l=mid+1;
        }
        
        System.out.println("-1");
        System.out.print(r);
    }
}

1227. 分巧克力 - 二分

1227. 分巧克力 - AcWing题库

思路:

若边长c不能满足分块需求,则大于c的都不能满足,具有单调性,可以二分边长

check(mid)函数负责判断边长mid是否可以满足分块需求

自己ac的呜呜呜,好久没写代码了

import java.util.*;

public class Main{
    static int n,k;
    static int N=(int) 1e5+10;
    static int[] h=new int[N],w=new int[N];
    
    public static boolean ck(int mid) //如果不满足则返回ture
    {
        int sum=0;
        
        for(int i=0;i<n;i++)
        {
            if(h[i]<mid||w[i]<mid) continue;
            sum+=(h[i]/mid)*(w[i]/mid);
        }
        if(sum>=k) return false;
        return true;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        k=sc.nextInt();
        int max=0;
        for(int i=0;i<n;i++) 
        {
            h[i]=sc.nextInt();
            w[i]=sc.nextInt();
            max=Math.max(h[i],Math.max(w[i],max));
        }
        
        int l=1,r=max;
        while(l<r)
        {
            int mid=l+r>>1;
            if(ck(mid)) r=mid;
            else l=mid+1;
        }
        if(ck(r)) System.out.print(r-1);
        else System.out.print(r);

    }
}

5407. 管道 - 二分+区间合并

5407. 管道 - AcWing题库

思路:

       自己写的时候用二分+差分,但是数据范围爆了

       因为若某时刻可以将管道灌满,则该时刻之后的任何时刻均不为答案,具有单调性,因此可以对时刻进行二分。因为差分会爆数据,所以采用区间合并,计算每一个阀门在时刻mid的覆盖区间,并将这些区间进行合并。

下面是wa1代码↓ 

import java.util.*;

public class Main
{
    static int n,len;
    static int N=(int)1e5+10;
    static int[] L=new int[N],S=new int[N],P=new int[N];
    static long[] b=new long[N];
    
    public static boolean ck(int mid)
    {
        for(int i=1;i<=len;i++) b[i]=P[i]-P[i-1];
        
        for(int i=1;i<=n;i++)
        {
            int ll=Math.max(1,L[i]-(mid-S[i]));
            int rr=Math.min(len,L[i]+(mid-S[i]));
            b[ll]+=1;
            b[rr+1]-=1;
        }
        
        for(int i=1;i<=len;i++)
        {
            b[i]+=b[i-1];
            if(b[i]<1) return false;
        }
        return true;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        len=sc.nextInt();
        for(int i=1;i<=n;i++)
        {
            L[i]=sc.nextInt();
            S[i]=sc.nextInt();
        }
        int l=1,r=(int)1e5;
        while(l<r)
        {
            int mid=l+r>>1;
            if(ck(mid)) r=mid;
            else l=mid+1;
        }
        System.out.print(r);
    }
}

正确代码

import java.util.*;

public class Main
{
    static int n,len;
    static int N=(int)1e5+10;
    static int[] L=new int[N],S=new int[N];
    
    public static boolean ck(int mid)
    {
        List<PII> p=new ArrayList<>();
        for(int i=1;i<=n;i++)
            if(mid>=S[i])
            {
                int t=mid-S[i]; //时间差
                int st=Math.max(1,L[i]-t);
                int ed=Math.min(len,L[i]+t);
                p.add(new PII(st,ed));
            }
            
        Collections.sort(p); //按左边界值从小到大排好
        int st=-1,ed=-1;
        for(PII t:p)
        {
            if(t.x<=ed+1) //如果当前左边界≤正在维护的左边界-1 
                ed=Math.max(ed,t.y); //区间合并
            else{
                st=t.x;
                ed=t.y;
            }
        }
        return st==1&&ed==len;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        len=sc.nextInt();
        for(int i=1;i<=n;i++)
        {
            L[i]=sc.nextInt();
            S[i]=sc.nextInt();
            //System.out.println(L[i]+" "+S[i]);
        }
        int l=1,r=(int)2e9; //最晚在10^9时刻打开第一个阀门,经过2*10^9才灌满
        while(l<r)
        {
            int mid=(int)((long)l+r>>1);
            if(ck(mid)) r=mid;
            else l=mid+1;
        }
        System.out.print(l);
    }
}

class PII implements Comparable<PII> //按第一个值从小到大排序
    {
        int x,y;
        public PII(int x,int y)
        {
            this.x=x;
            this.y=y;
        }
        public int compareTo(PII t)
        {
            return this.x-t.x;
        }
    }

4956. 冶炼金属 - 二分最大最小值

4956. 冶炼金属 - AcWing题库

思路:

       刚开始思路就是二分,但是纵观整个区间,V值并没有单调性(只有中间一段符合要求),所以我刚开始写了一个半二分,即前段暴力枚举后段二分,然后就tle了。

      正解即分别二分最大最小值,利用了二分模板特性。

7/10 - 半二分 

import java.util.*;
import java.io.*;
import java.math.*;

public class Main 
{
	static int n;
	static int N=(int)1e4+10;
	static PII[] w=new PII[N];
	
	public static boolean ck(int mid)
	{
		for(int i=0;i<n;i++)
		{
			if((int)(Math.floor(w[i].x/mid))!=w[i].y) return false;
		}
		return true;
	}
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		int maxx=0;
		for(int i=0;i<n;i++)
		{
			int a=sc.nextInt();
			int b=sc.nextInt();
			maxx=Math.max(maxx,a);
			w[i]=new PII(a,b);
		}
		
		int ll=0;
		for(int i=1;i<maxx;i++)
		{
			if(ck(i)) {
				ll=i;break;
			}
		}
		
		int l=ll,r=(int)maxx;
		while(l<r)
		{
			int mid=(int)((long)l+r>>1);
			if(!ck(mid)) r=mid;
			else l=mid+1;
		}
		System.out.print(ll+" "+(r-1));
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x,o.x);
	}
}

 二分最小最大值 ac



import java.util.*;
import java.io.*;
import java.math.*;

public class Main 
{
	static int n;
	static int N=(int)1e4+10;
	static PII[] w=new PII[N];
	
	public static boolean ck1(int mid)
	{
		for(int i=0;i<n;i++)
			if((int)(Math.floor(w[i].x/mid))>w[i].y) 
				return false;
		return true;
	}
	
	public static boolean ck2(int mid)
	{
		for(int i=0;i<n;i++)
			if((int)(Math.floor(w[i].x/mid))<w[i].y) 
				return false;
		return true;
	}
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		int maxx=0;
		for(int i=0;i<n;i++)
		{
			int a=sc.nextInt();
			int b=sc.nextInt();
			maxx=Math.max(maxx,a);
			w[i]=new PII(a,b);
		}
		
		int l=1,r=maxx;
		while(l<r)
		{
			int mid=l+r>>1;
			if(ck1(mid)) r=mid;
			else l=mid+1;
		}
		System.out.print(l+" ");
		
		r=maxx;
		while(l<r)
		{
			int mid=l+r+1>>1;
			if(ck2(mid)) l=mid;
			else r=mid-1;
		}
		System.out.print(r);
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x,o.x);
	}
}

789. 数的范围 - 二分最大最小值

活动 - AcWing

二分模板题



import java.util.*;
import java.io.*;
import java.math.*;

public class Main 
{
	static int n,t;
	static int N=(int)1e5+10;
	static int[] a=new int[N];
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		t=sc.nextInt();
	
		for(int i=0;i<n;i++) a[i]=sc.nextInt();
		
		int ll=0,rr=0;
		while(t-->0)
		{
			int x=sc.nextInt();
			int l=0,r=n-1;
			while(l<r)
			{
				int mid=l+r>>1;
				if(a[mid]>=x) r=mid;
				else l=mid+1;
			}
			ll=l;
			
			r=n-1;
			while(l<r)
			{
				int mid=l+r+1>>1;
				if(a[mid]<=x) l=mid;
				else r=mid-1;
			}
			rr=l;
			
			if(a[l]!=x) {ll=-1;rr=-1;}
			System.out.println(ll+" "+rr);
		}
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x,o.x);
	}
}

1236. 递增三元组 - 二分

1236. 递增三元组 - AcWing题库

思路:

       给abc三个数组排序,枚举b数组,二分找a数组中最后一个小于b[i]的位置,再二分找c数组中第一个大于b[i]的位置,下面以枚举b[0]=5为例:

1    4    5

5    5    9

4    6    7

       则b[0]=5满足条件的三元组个数为2×2=4个,方法即二分模板

import java.util.*;

public class Main{
    static int n;
    static int N=(int)1e5+10;
    static int[] a=new int[N],b=new int[N],c=new int[N];
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        for(int i=1;i<=n;i++)
            a[i]=sc.nextInt();
        for(int i=1;i<=n;i++)
            b[i]=sc.nextInt();
        for(int i=1;i<=n;i++)
            c[i]=sc.nextInt();
        Arrays.sort(a,1,n+1);
        Arrays.sort(b,1,n+1);
        Arrays.sort(c,1,n+1);
        
        long res=0;
        int l,r;
        for(int i=1;i<=n;i++)
        {
            l=0;r=n;
            long s=1;        
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(a[mid]<b[i]) l=mid;
                else r=mid-1;
            }
            if(l==0) continue; //如果没找到比b[i]小的
            s*=l;
            l=1;r=n+1;
            while(l<r)
            {
                int mid=l+r>>1;
                if(c[mid]>b[i]) r=mid;
                else l=mid+1;
            }
            if(r==n+1) continue; //如果没找到比b[i]大的
            s*=(n-r+1);
            res+=s;
        }
        System.out.print(res);
    }
}

P3 - 前缀和

★一维前缀和模板

a数组下标从1开始,S[i] = S[i-1] + a[i]

 [ Al,Ar ]段的和 = s[r] - s[l-1]

for(int i=1;i<=n;i++)
{
    a[i]=sc.nextInt();
    s[i]=s[i-1]+a[i];
}
        
[Al,Ar]的和 = s[r]-s[l-1]

★二维前缀和模板 

static int N=1010;
static int[][] a=new int[N][N],s=new int[N][N];
    
for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        a[i][j]=sc.nextInt();
        s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
    }

while(q-->0)
{
    int x1=sc.nextInt(),y1=sc.nextInt(),x2=sc.nextInt(),y2=sc.nextInt();
 
    int res=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
 
    System.out.println(res);
}

1230. K倍区间 - 一维前缀和+数学

1230. K倍区间 - AcWing题库

思路:

       题目要求所有【连续子序列之和能整除k】的区间个数,则求和易想到前缀和,根据题目条件即满足(s[r]-s[l-1])%k==0,变换等式即s[r]%k==s[l-1]%k,所以若s[i]%k的值与前面s[i-t]%k的值相同,说明[i-t,i]这一段之和可以整除k,即为一个k倍区间。

      最后要加上cnt[0],因为前面循环只考虑了两个取余相等的情况,单个s[i]%k=0的情况也需要考虑进去。

import java.util.*;
import java.io.*;
import java.math.*;

public class Main 
{
	static int n,k;
	static long res;
	static int N=(int)1e5+10;
	static long[] s=new long[N];
	static long[] cnt=new long[N]; //记录s[i]%k值的个数
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		k=sc.nextInt();
	
		for(int i=1;i<=n;i++) 
		{
			s[i]=sc.nextInt();
			s[i]+=s[i-1];
		}
		
		for(int i=1;i<=n;i++)
		{
			res+=cnt[(int)(s[i]%k)];  //若s[r]%k的值和之前的s[i]%k相等,说明[i,r]是一个k倍区间
			cnt[(int)(s[i]%k)]++; //记录当前s[i]%k的值
		}
		System.out.print(res+cnt[0]); //最后要加上单个余数为0的情况
		
		
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x,o.x);
	}
}

 4405. 统计子矩阵 - 二维前缀和

4405. 统计子矩阵 - AcWing题库

思路:

        题目要求找该矩阵所有子矩阵和小于k的个数,则矩阵求和易想到二维前缀和模板。但是数据范围长宽都为500,4个for循环会TLE。因此我们限定上下界,由于矩阵数均为正数,因此区域越小,和越小,所以我们枚举右边界,移动左边界,直到区域之和小于k,则满足条件的子矩阵个数即为r-l+1。

import java.util.*;

public class Main{
    static int N=510;
    static int n,m,k;
    static int[][] s=new int[N][N],a=new int[N][N]; 
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int k=sc.nextInt();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                a[i][j]=sc.nextInt();
                s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
            }
        
        long res=0;
        for(int u=1;u<=n;u++)
            for(int d=u;d<=n;d++) //固定上下界
                for(int l=1,r=1;r<=m;r++)
                {
                    while(l<=r&&s[d][r]-s[u-1][r]-s[d][l-1]+s[u-1][l-1]>k) l++;
                    //因为矩阵都为正数,则缩小范围,具有单调性
                    if(l<=r) res+=r-l+1;
                }
        System.out.print(res);
        
    }
}

99. 激光炸弹 - 二维前缀和

活动 - AcWing

思路:

       由于题目目标位置是以坐标呈现(x,y),而爆炸区域却是方块,为了方便,我们可以设目标位置也是单独的一个小方块

       题目要求一颗炸弹能炸掉目标的最大价值,则我们可以先记录每一块的价值,然后用二维前缀和预处理,再遍历所有大小为R×R区域,取最大价值

import java.util.*;

public class Main{
    static int N=5010;
    static int n,r;
    static int[][] s=new int[N][N];
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        r=sc.nextInt();
        r=Math.min(r,5001);
        for(int i=0;i<n;i++)
        {
            int x=sc.nextInt();
            int y=sc.nextInt();
            int w=sc.nextInt();
            s[++x][++y]+=w;  //前缀和下标从1开始,所以x和y都要+1
        }

        for(int i=1;i<=5001;i++)
            for(int j=1;j<=5001;j++)
                s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        
        int res=0;
        for(int i=r;i<=5001;i++)
            for(int j=r;j<=5001;j++)
                res=Math.max(res,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);
        System.out.print(res);
    }
}

P4 - 差分

★一维差分模板

给a数组 [l,r] 区间的每个数+c,只需要给其差分数组b做如下操作即可

b[l]+=c;
b[r+1]-=c;

构造差分数组   \large b\left [ i \right ]=a\left [ i \right ]-a\left [ i-1 \right ] 

for(int i=1;i<=n;i++)
{
    a[i]=sc.nextInt();
    b[i]=a[i]-a[i-1]; //构造差分数组
}

差分数组进行  \large b\left [ l \right ]+=c    \large b\left [ r+1 \right ]-=c 操作

while(k-->0)
{
    b[l]+=c;
    b[r+1]-=c;
}

最后求差分数组b的前缀和,即为原数组在【l,r】段+c的数组

for(int i=1;i<=n;i++)
{
    a[i]=a[i-1]+b[i]; //b的前缀和是a
    System.out.print(a[i]+" ");
}

★二维差分模板

初始化

static int N=1010;
static int[][] a=new int[N][N],b=new int[N][N];
 
public static void work(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
 
 
for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        a[i][j]=sc.nextInt();
        work(i,j,i,j,a[i][j]);
    }
 

 求前缀和

work(x1,y1,x2,y2,c);
 
for(int i=1;i<=n;i++)
{
    for(int j=1;j<=m;j++)
    {
        b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
        System.out.print(b[i][j]+" ");
    }
    System.out.println();
}

5396.棋盘 - 二维差分

5396. 棋盘 - AcWing题库

思路:

       题目求二维区域翻转t次黑白子情况,我们只需要记录每个棋子的翻动次数即可,因为初始是白棋全为0,则结束所有翻动后,若该位置翻动次数是偶数,则说明该位置为白棋,否则为黑棋。二维区域加次数,易想到二维差分模板。

import java.util.*;

public class Main{
    static int N=2010;
    static int[][] a=new int[N][N],b=new int[N][N];
    static int n,t;
    
    public static void work(int x1,int y1,int x2,int y2,int c)
    {
        b[x1][y1]+=c;
        b[x1][y2+1]-=c;
        b[x2+1][y1]-=c;
        b[x2+1][y2+1]+=c;
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        t=sc.nextInt();
        
        while(t-->0)
        {
            int x1=sc.nextInt(),y1=sc.nextInt(),x2=sc.nextInt(),y2=sc.nextInt();
            work(x1,y1,x2,y2,1);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
                System.out.print(b[i][j]%2);
            }
            System.out.println();
        }
    }
}

 4655. 重新排序 - 差分 + 排序

4655. 重新排序 - AcWing题库

思路:

题目要求重新排列后区间累加最大和,对于样例

5
1 2 3 4 5
2
1 3
2 5

我们发现未排序前区间累加和是

2  3

    2  3  4  5

而排列后最大累加和是

4  5  

    4  5  2  3

不难发现,需要累加和最大,即让重叠区域放大的数,思路有了,如何实现呢?

我们可以用差分记录重叠区域个数x,然后通过从小到大排序让最大的x个数乘2即可

——————————————————————————————————————

上述样例差分数组b[i]和原数组a[i]

2  2  1  1

2  3  4  5

未排序区间累加和为:s1=b[i]*a[i]=1*1+2*2+2*3+1*4+1*5=20

差分数组和原数组排列后

1  1  1  2  2

1  2  3  4  5

排序后区间累加和为:s2=b[i]*a[i]=1*1+1*2+1*3+2*4+2*5=24

import java.util.*;

public class Main{
    static int N=(int)1e5+10;
    static int n,m;
    static int[] a=new int[N],s=new int[N];
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        for(int i=1;i<=n;i++) a[i]=sc.nextInt();
        
        m=sc.nextInt();
        while(m-->0)
        {
            int l=sc.nextInt(),r=sc.nextInt();
            s[l]++;
            s[r+1]--;
        }
        
        long s1=0,s2=0;
        for(int i=1;i<=n;i++) s[i]+=s[i-1];

        for(int i=1;i<=n;i++) s1+=(long)s[i]*a[i];
        Arrays.sort(s,1,n+1);
        Arrays.sort(a,1,n+1);
        for(int i=1;i<=n;i++) s2+=(long)s[i]*a[i];
        System.out.print(s2-s1);
    }
}

P5 - 双指针

799. 最长连续不重复子序列 - 滑动窗口

活动 - AcWing

思路:

       本题我们可以假设一个框框,我们枚举右边界,如果框框内出现重复数字,则缩小左边界直至框框内没有重复数字,滑动窗口用双指针来实现。

1  2  2  3  5  (发现框框内有重复!左边界缩小至框框内无重复)

1  2  2  3  5   

1  2  2  3  5  

import java.util.*;

public class Main
{
    static int N=(int)1e5+10;
    static int[] a=new int[N],cnt=new int[N];
    static int n;
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        
        for(int i=0;i<n;i++) a[i]=sc.nextInt();
        int maxx=0;
        int l=0,r=0;
        for(r=0;r<n;r++)
        {
            cnt[a[r]]++;
            while(l<r&&cnt[a[r]]>1) cnt[a[l++]]--;
            maxx=Math.max(maxx,r-l+1);
        }
        System.out.print(maxx);
    }
}

800. 数组元素的目标和 - 二分 / 双指针

活动 - AcWing

思路:

       题目要求两个有序数组a[i]+b[j]=x,输出下标(i,j)

       第一次一眼二分,写完之后捏了个数据发现不对呀,好像二分只能输出一个答案,于是删掉重写了个双指针,实现了输出所有答案——但是题目写着“保证唯一解”,然后就TLE了

二分和双指针代码都贴一下

双指针(12 / 16)

import java.util.*;

public class Main{
    static int n,m,x;
    static int N=(int)1e5+10;
    static int[] a=new int [N],b=new int[N];
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        x=sc.nextInt();
        for(int i=0;i<n;i++)
        {
            a[i]=sc.nextInt();
            a[i]=x-a[i];
        } 
        for(int i=0;i<m;i++) b[i]=sc.nextInt();
        for(int i=0,j=0;i<n;i++)
        {
            while(b[j]<a[i]&&j<m) j++;
            while(b[j++]==a[i]&&j<m) System.out.println(i+" "+(j-1));
            j=0;
        }
        
    }
}

二分ac

import java.util.*;

public class Main{
    static int n,m,x;
    static int N=(int)1e5+10;
    static int[] a=new int [N],b=new int[N];
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        x=sc.nextInt();
        for(int i=0;i<n;i++)
        {
            a[i]=sc.nextInt();
            a[i]=x-a[i];
        } 
        for(int i=0;i<m;i++) b[i]=sc.nextInt();
        for(int i=0;i<n;i++)
        {
            int l=0,r=m-1;
            while(l<r)
            {
                int mid=l+r>>1;
                if(b[mid]>=a[i]) r=mid;
                else l=mid+1;
            }
            if(b[l]==a[i]) 
            {
                System.out.print(i+" "+l);
                break;
            }
        }
    }
}

2816. 判断子序列 - 双指针

活动 - AcWing

思路:

       st指针指向a序列的元素,直接在b序列里找a对应的元素,如果找到了st++,最后看st有没有把a序列全部指一遍

import java.util.*;

class Main
{
    static int N=(int)1e5+10;
    static int n,m;
    static int[] a=new int[N],b=new int[N];
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        
        n=sc.nextInt();
        m=sc.nextInt();
        for(int i=0;i<n;i++) a[i]=sc.nextInt();
        for(int j=0;j<m;j++) b[j]=sc.nextInt();
        
        int st=0;
        for(int i=0;i<m;i++)
        {
            if(a[st]==b[i]) st++;
            if(st>=n) break;
        }
        if(st==n) System.out.print("Yes");
        else System.out.print("No");
    }
}

1238. 日志统计 - 滑动窗口

1238. 日志统计 - AcWing题库

思路:

       题目表明在[T,T+D)时间段内点赞不少于k个的帖子为热帖,要求输出热帖id

我们可以将所有记录用二维数组存储,按时间从小到大排序

       维护滑动窗口,使窗口内的时间段不超过时限(即框起来的时间为[T,T+D)),统计窗口内的ID,如果超过时限,则缩小左边界,使窗口内时限合法

import java.util.*;

class Main{
    static int n,d,k;
    static int N=(int)1e5+10;
    static int[] cnt=new int[N],st=new int[N];
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        d=sc.nextInt();
        k=sc.nextInt();
        int[][] a=new int[n][2];
        for(int i=0;i<n;i++)
        {
            a[i][0]=sc.nextInt(); //时刻
            a[i][1]=sc.nextInt(); //id号
        }
        Arrays.sort(a,(o1,o2)->{return o1[0]-o2[0];}); //按时间从小到大排序
        
        for(int l=0,r=0;r<n;r++)
        {
            int id=a[r][1];
            cnt[id]++;
            while(a[r][0]-a[l][0]>=d)
            {
                cnt[a[l][1]]--;
                l++;
            }
            if(cnt[id]>=k) st[id]=1;
        }
        for(int i=0;i<=100000;i++) if(st[i]==1) System.out.println(i);
    }    
}

class PII implements Comparable<PII>
{
    int x,y;
    public PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
    public int compareTo(PII o)
    {
        return Integer.compare(x,o.x);
    }
    
}

P6 - BFS/DFS

1233. 全球变暖 - BFS

1233. 全球变暖 - AcWing题库

思路:

       BFS模板题,从某块陆地开始bfs(向四周扩展+标记),如果发现这个岛屿只要存在一块陆地四周无海洋(#四周无.),则这就是一块未沉的岛屿。题目要求计算有多少岛屿沉没,则我们可以用【岛屿总数-未沉没岛屿数】求的。

      关于岛屿总数怎么求,因为bfs从某块陆地开始扩展,该陆地的所有岛屿都会被bfs蔓延标记,因此主函数中循环里只有存在未标记的陆地块,岛屿数++。换句话说,对某块陆地进行bfs后,这个陆地所在岛屿上所有陆地都会被标记,所以若此后出现未标记的陆地,肯定是新的一座岛屿。

package abcd;
import java.util.*;

public class lanqiaoo {
	static int N=(int)1010;
	static int n,res,island;
	static char[][] g=new char[N][N];
	static int[][] st=new int[N][N]; 
	static int[] dx={1,-1,0,0},dy= {0,0,1,-1};
	
	public static void bfs(int x,int y)
	{
		Queue<PII> q=new LinkedList<>();
		q.offer(new PII(x,y));
		st[x][y]=1;
		boolean f=false; 
		
		while(!q.isEmpty())
		{
			int cnt=0; //统计(x,y)这块地四周土地的个数
			PII t=q.poll();
			for(int i=0;i<4;i++)
			{
				int nx=t.x+dx[i],ny=t.y+dy[i];
				if(nx<0||nx>=n||ny<0||ny>=n||g[nx][ny]=='.') continue;
				cnt++;
				if(st[nx][ny]==0)
				{
					q.offer(new PII(nx,ny));
					st[nx][ny]=1;
				}
			}
			if(cnt==4&&f==false) //只统计第一次,因为一个岛上可能有多个陆地
			{
				res++;
				f=true;
			}
		}
	}
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner (System.in);
		n=sc.nextInt();
		for(int i=0;i<n;i++)
		{
			String s=sc.next();
			g[i]=s.toCharArray();
		}
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				char x=g[i][j];
				if(x=='#'&&st[i][j]==0) 
				{
					bfs(i,j);
					island++;
				}
			}
		}
		System.out.print(island-res); //沉没的岛屿数=总岛屿数-未沉没的岛屿数
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x, o.x);
	}
}

P7 - 区间合并

★区间合并模板

step1:将每个区间按左端点从小到大进行排序

step2:如图所示,可分3种情况

  • 情况一:当前区间完全被上一区间覆盖,直接跳过
  • 情况二:将当前区间的右端点更新为上一区间的右端点,达到区间延长的效果
  • 情况三:当前区间的左端点严格大于上一区间的右端点,则表示该区间不能合并,更新区间且count++

803. 区间合并

活动 - AcWing

import java.util.*;

class Main{
    static int n;
    static int N=(int)1e5+10;
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        List<PII> a=new ArrayList<>();
        n=sc.nextInt();
        for(int i=0;i<n;i++)
        {
            int l=sc.nextInt(),r=sc.nextInt();
            a.add(new PII(l,r));
        }
        Collections.sort(a);
        int res=0;
        int st=Integer.MIN_VALUE,ed=Integer.MIN_VALUE;
        for(int i=0;i<a.size();i++)
        {
            int l=a.get(i).x,r=a.get(i).y;
            if(l>ed) //两区间没重合
            {
                res++;
                st=l;
                ed=r;
            }
            else //区间重合,ed取最大,达到延长区间效果
            {
                ed=Math.max(ed,r);
            }
        }
        System.out.println(res);
    }    
}

class PII implements Comparable<PII>
{
    int x,y;
    public PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
    public int compareTo(PII o)
    {
        return Integer.compare(x,o.x);
    }
    
}

422. 校门外的树 -  模拟+区间合并

422. 校门外的树 - AcWing题库

import java.util.*;

class Main{
    static int n,len;
    static int N=(int)1e4+10;
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        List<PII> a=new ArrayList<>();
        len=sc.nextInt();
        n=sc.nextInt();
        for(int i=0;i<n;i++)
        {
            int l=sc.nextInt();
            int r=sc.nextInt();
            a.add(new PII(l,r));
        }
        Collections.sort(a);
        
        int res=0;
        int st=a.get(0).x,ed=a.get(0).y;
        for(int i=1;i<a.size();i++)
        {
            int l=a.get(i).x,r=a.get(i).y;
            if(l>ed) //两区间没重合
            {
                res+=ed-st+1;
                st=l;
                ed=r;
            }
            else //区间重合,ed取最大,达到延长区间效果
            {
                ed=Math.max(ed,r);
            }
        }
        res+=ed-st+1;
        
        System.out.println(len+1-res);
    }    
}

class PII implements Comparable<PII>
{
    int x,y;
    public PII(int x,int y)
    {
        this.x=x;
        this.y=y;
    }
    public int compareTo(PII o)
    {
        return Integer.compare(x,o.x);
    }
    
}

P8 - 日期问题

2867. 回文日期 - 回文串+日期模拟

2867. 回文日期 - AcWing题库

思路:

         简单粗暴,ck1判断回文串,ck2判断ABABBABA型回文串,year判断是否为正确日期

注意:回文串和ABABBABA型串可能是同一天,第一行输出回文串,第二行输出ABABBABA型串,还要注意日期合法(自己做上面的坑全踩了)


import java.util.*;

public class Main {
	static int N=(int)1e5+10;
	static int n,m;
	
	public static boolean ck1(String s)
	{
		for(int i=0;i<s.length()/2;i++)
		{
			int j=s.length()-1-i;
			if(s.charAt(i)!=s.charAt(j)) return false;
		}
		return true;
	}
	
	public static boolean ck2(String s)
	{
		String a=s.substring(0,4),b=s.substring(4,8);
		StringBuffer bb=new StringBuffer(b);
		b=String.valueOf(bb.reverse());
		char x1=a.charAt(0),x2=a.charAt(1),x3=a.charAt(2),x4=a.charAt(3);
		if(b.equals(a)&&x1!=x2&&x3!=x4&&x1==x3&&x2==x4) return true;
		return false;
	}
	
	public static boolean run(int y)
	{
	    if(y%400==0||(y%4==0)&&(y%100!=0)) return true;
	    return false;
	}
	
	public static boolean year(String s)
	{
	    String y=s.substring(0,4),m=s.substring(4,6),d=s.substring(6);
	    int yy=Integer.parseInt(y),mm=Integer.parseInt(m),dd=Integer.parseInt(d);
	    if(mm==0||mm>12||dd==0||dd>31) return false;
	    if(mm==2)
	    {
	        if(run(yy)&&dd>29) return false;
	        else if(!run(yy)&&dd>28) return false;
	    }
	    if((mm==4||mm==6||mm==9||mm==11)&&dd>30) return false;
	    return true;
	    
	}
	
	public static void main(String[] args)
	{
		Scanner sc=new Scanner (System.in);
		n=sc.nextInt();
		int st1=0,st2=0;
		String s=String.valueOf(n);
		for(int i=n+1;i<=99999999;i++)
		{
			if(st1==1&&st2==1) break;
			String t=String.valueOf(i);
			if(!year(t)) continue;
			if(ck1(t)&&st1==0)
			{
				st1++;
				System.out.println(t);
				if(ck2(t))
				{
				    st2++;
				    System.out.print(t);
				    break;
				}
			}
			if(ck2(t)&&st2==0)
			{
				st2++;
				System.out.println(t);
			}
		}
	}
}

class PII implements Comparable<PII>
{
	int x,y;
	public PII(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	public int compareTo(PII o)
	{
		return Integer.compare(x, o.x);
	}
}

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值