常用算法模板

常用数据结构API

队列

普通队列

static Queue<Integer> q=new LinkedList<>(); //队列
public static void main(String[] args) {
    q.offer(1); //入队
    q.offer(2);
    q.offer(3);
    System.out.println(q.peek()); //出队
    System.out.println(q.poll()); //出队并删除
}

优先队列

static Queue<Integer> q=new PriorityQueue<>(new Comparator<Integer>() { 
    //Java中默认就是小根堆,所以要重写compare方法,可以改成大根堆
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }	
});	

栈

static Deque<Integer> s=new ArrayDeque<>(); //栈
public static void main(String[] args) {
    s.push(1);	//入栈
    s.push(2);
    s.push(3);
    System.out.println(s.peek()); //出栈
    System.out.println(s.pop()); //出栈并删除
}

链表

import java.util.LinkedList;

public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList<String> animals = new LinkedList<String>();
        //普通添加
        animals.add("cow");
        System.out.println("使用add添加动物后:"+animals);
        // 使用 addFirst() 在头部添加元素
        animals.addFirst("mouse");
        System.out.println("使用addFirst添加动物后"+animals);
        //删除
       	animals.removeFirst();
        System.out.println("使用removeFirst删除动物后"+animals);
        animals.removeLast();
        System.out.println("使用removeLast删除动物后"+animals);
        //查找
        System.out.println("使用getFirst获取到的动物是:"+animals.getFirst());
        System.out.println("使用getLast获取到的动物是:"+animals.getLast());
    }
}

哈希表

TreeMap

package 天梯赛2023;

import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

public class Test {
//	static Map<Integer, Integer> map=new TreeMap<>(new Comparator<Integer>() {	//只能对key实现自定义排序
//		public int compare(Integer o1,Integer o2) {
//			return o2-o1;
//		}
//	});
	
	static Map<String, Integer> map=new TreeMap<>();
	public static void main(String[] args) {
		map.put("zju", 30);
		map.put("hdu", 93);
		map.put("pku", 39);
		map.put("hbu", 42);
		
		//map.forEach((key,value)->{
		//	System.out.println(key+":"+value);
		//});
		
		int a=map.get("zju")-10;		//map中元素每次操作并删除后重新手动添加
		map.remove("zju");
		map.put("zju", a);
		System.out.println("------------------");
		
		//map.forEach((key,value)->{		//无法在内部进行定义其他变量,所以不推荐使用
		//	System.out.println(key+":"+value);
		//});
		//map.replace("zju", 30);
		
		System.out.println("------------------");
		
		for(Entry<String, Integer> m:map.entrySet()) {		//推荐使用!!
			System.out.println(m.getKey()+":"+m.getValue());
		}
	}
}

HashMap

static Map<String, Integer> ans=new HashMap<>(); //一种高效率的哈希表

LinkedHashMap

static Map<String, Integer> ans=new LinkedHashMap<>(); //能够按放入顺序输出

常用算法

数学

最大公约数

方法一:递归

static int gcd(int a, int b) {
    return b>0?gcd(b, a%b):a;
}

方法二:循环

public class GongYueShu2 {	//没有排除a或b为零的情况
		public static void main(String[]args) {
			Scanner in =new Scanner(System.in);
			int a =in.nextInt();
			int b =in.nextInt();
			int oa = a;
			int ob = b;
            int k = 0;
			while (b!=0)
			{
				k = a % b;
				a = b;
				b = k;
			}
			System.out.println(oa+"和"+ob+"的最大公约数是"+a);
	}
}

前缀和

image-20240323160441444

image-20240323160449114

#include <iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int s[N][N];
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }
    return 0;
}

差分

特征:

区间修改,单点查询

但是无论修改多少次,只有当所有修改操作结束了,才能查询(求前缀和),而且是单点查询

image-20240323160546033

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N]; 
int main()
{
    int n,m;
    scanf("%d%d", &n, &m);
    for(int i = 1;i <= n; i++) 
    {
        scanf("%d", &a[i]);
        b[i] = a[i] - a[i - 1];      //构建差分数组
    }
    int l, r, c;
    while(m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        b[l] += c;     //表示将序列中[l, r]之间的每个数 加或者减c
        b[r + 1] -= c;
    }
    for(int i = 1;i <= n; i++) 
    {
        b[i] += b[i - 1];  //求前缀和运算
        printf("%d ",b[i]);
    }
    return 0;
}

素数筛

埃式筛

复杂度:近似O(n)

public static void main(String[] args) {
    int n=sc.nextInt();
    Prime(n);
    for(int i=2;i<=n;i++) {
        if(!vis[i]) System.out.print(i+" ");
    }
}
public static void Prime(int  n){
    for(int i = 2; i <= n / i; i++){
        if(!vis[i]){
            for(int j = i * i; j <= n; j += i)
                vis[j]=true; // j是i的一个倍数,j是合数,筛掉。
        }
    }
}

复杂度:O(n)

欧拉筛

static void EulerSevie(int n)
{
    for(int i=2;i<=n;i++) {
        if(!visit[i]) {
            prime[++c]=i;		//++c让下面的<=c好写一些
        }
        for(int j=1;j<=c&&i*prime[j]<=n;j++) {
            visit[i*prime[j]]=true;
            if(i%prime[j]==0) break;//欧拉筛关键优化,避免合数被重复标记,比如12被2和6标记,后面又被3和4标记
        } 							//因此这里让合数始终被最小的质数标记,
    }
}

分解质因数

以下这道题有模板,需要记住!!并且有数学定理推论———唯一分解定理

http://t.csdnimg.cn/t4Jbt

牛客周赛Round31 #B

链接:https://ac.nowcoder.com/acm/contest/74362/B

题目描述

小红拿到了一个正整数x,她想知道x有多少个不同的素因子,你能帮帮她吗?

输入描述:

一个正整数x
1≤x≤10的13次方

输出描述:

x不同的素因子的数量。
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
	public static void main(String[] args) {
	Scanner sc=new Scanner(System.in);
	long n=sc.nextLong();
	int sum=0;
	for (int i = 2; i <= Math.sqrt(n); i++) {
		if(n%i==0) {
			sum++;
			while(n%i==0) {		//运用了埃式筛特点,把i的倍数给他筛掉
				n/=i;
			}
		}
	}
	if(n>1) {			//自己是素数的情况,得算上自己
		sum++;
	}
	System.out.println(sum);
}
}

这道题用传统的数组来做不行,会超过数组最大声明长度

快速幂

image-20240323155420490

long fastPower(long base, long power) { //mod=1000
    long result = 1;
    while (power > 0) {
        if (power % 2 == 1) {
            result = result * base % 1000;		
        }
        power /= 2;
        base = (base * base) % 1000;
    }
    return result;
}

排序

归并排序

void mergesort(int a[], int l, int r){
	if(l>=r) return;
	int mid = l+r>>1;
	mergesort(a, l, mid);
	mergesort(a, mid+1, r);
	int i,j,k = l;
	for(i = l, j = mid + 1; i <= mid&&j <= r;){
		if(a[i] > a[j]){
			b[k++] = a[j];
			j++;
		}
		else b[k++] = a[i], i++;
	} 
	while(i <= mid) b[k++] = a[i++];
	while(j <= r) b[k++] = a[j++]; 
	for(int ii = l; ii <= r; ii++){
		a[ii] = b[ii];
	}
}

二分

r=mid

int erfen(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1; //除2 操作
        if (check(mid)) r = mid; //check函数即为边界的选择
        else l = mid + 1;
    }
    return l; //l r都可以,跳出while r = l
}

l=mid

int erfen(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1; //注意+1操作
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

字符串处理

日期处理

//每月天数预存储
static int[] M= {0,31,28,31,30,31,30,31,31,30,31,30,31};
static int y,m,d;	//可设置初始年月日
//初始日期自己设置y,m,d  while条件设置目标日
while(y!=10000||m!=12||d!=31) {
    if(y%400==0||(y%4==0&&y%100!=0)){	//处理闰年
        M[2]=29;
    }else {
        M[2]=28;
    }
    d++;	//升天数
    if(d>M[m]) {//判断日期是否合法
        m++;//升月份
        d=1;
    }
    if(m>12) {//判断月份是否合法
        m=1;
        y++;//升年份
    }
    //此处调用check函数,判断当天是否合法
    check(y,m,d);
}

DFS

排列型枚举

答案版

package Week2Digui;
import java.util.*;

public class Main1 { //递归实现排列型枚举(DFS)
	static int N=510;
	static Scanner scanner=new Scanner(System.in);
	static boolean vis[]=new boolean[N];
	static int ans,r,n;
	
	public static void dfs(int u) {
		if(u>r) {
			ans++;
			return;
		}
		for(int i=1;i<=n;i++) {
			if(!vis[i]) {
				vis[i]=true;
				dfs(u+1);
				vis[i]=false;
			}
		}
	}
	
	public static void main(String[] args) {
		n=scanner.nextInt();
		r=scanner.nextInt();
		dfs(1);
		System.out.println(ans);
	}
}

打印版

package Week2Digui;
import java.util.*;

public class Main1 { //递归实现排列型枚举(DFS)
	static int N=510;
	static Scanner scanner=new Scanner(System.in);
	static boolean vis[]=new boolean[N];
	static int a[]=new int[N];
	static int ans,r,n;
	
	public static void dfs(int u) {
		if(u>r) {
			for(int i=1;i<u;i++) {
				System.out.print(a[i]+" ");
			}
			System.out.print("\n");
			ans++;
			return;
		}
		for(int i=1;i<=n;i++) {
			if(!vis[i]) {
				vis[i]=true;
				a[u]=i;
				dfs(u+1);
				vis[i]=false;
			}
		}
	}
	
	public static void main(String[] args) {
		n=scanner.nextInt();
		r=scanner.nextInt();
		dfs(1);
		System.out.println(ans);
	}
}

组合型枚举

答案版

package Part3;

import java.util.Scanner;

public class Test{
	static int N;		//1~N的数字中
	static int m;		//选取m个数字
	static int ans;		//记录组合的个数
	
	public static void dfs(int u,int start){		//从第u层开始搜,从数字start开始往下扩展
		if(u > m){		//当 u > m 时,证明已经搜够了m个数
			ans++;
			return;
		}
		for(int i = start;i <= N;i++){
			dfs(u+1,i+1);		//此处的恢复现场不是必须的,因为每一次循环,path[]的值都被覆盖了
		}
	}
	
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		N = input.nextInt();
		m = input.nextInt();
		dfs(1,1);
		System.out.println(ans);
	}
}

打印版

import java.util.Scanner;

public class dfs组合型枚举{
	static int[] path = new int[30];		//保存路径数组
	static int N;		//1~N的数字中
	static int m;		//选取m个数字
	static int ans;		//记录组合的个数
	
	public static void dfs(int u,int start){		//从第u层开始搜,从数字start开始往下扩展
		if(u > m){		//当 u > m 时,证明已经搜够了m个数
			for(int i = 1;i <= m;i++){
				System.out.print(path[i] + " ");
			}
			System.out.println();
			ans++;
			return;
		}
		for(int i = start;i <= N;i++){
			path[u] = i;
			dfs(u+1,i+1);
			path[u] = 0;		//此处的恢复现场不是必须的,因为每一次循环,path[]的值都被覆盖了
		}
	}
	
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		N = input.nextInt();
		m = input.nextInt();
		
		dfs(1,1);
		System.out.println(ans);
	}
}

指数型枚举

答案版

package Week2Digui;
import java.util.*;

public class Main{         //AcWing 92. 递归实现指数型枚举(DFS)
    static int N=20;
    static int n;
    static int ans;
    static boolean vis[]=new boolean[N];
    static Scanner sc=new Scanner(System.in);
    
    public static void dfs(int u){
        if(u>n){
            ans++;
            return;
        }
            dfs(u+1);
            dfs(u+1);		
    }
    
    public static void main(String[] args){
        n=sc.nextInt();
        dfs(1);
        System.out.println(ans);
    } 
}

打印版

package Week2Digui;
import java.util.*;

public class Main{         //AcWing 92. 递归实现指数型枚举(DFS)
    static int N=20;
    static int n;
    static int ans;
    static boolean vis[]=new boolean[N];
    static Scanner sc=new Scanner(System.in);
    
    public static void dfs(int u){
        if(u>n){
            for(int i=1;i<=n;i++){
                if(vis[i])
                System.out.print(i+" ");
            }
            System.out.print("\n");
            ans++;
            return;
        }
            vis[u]=true;
            dfs(u+1);
            vis[u]=false;
            dfs(u+1);		
    }
    
    public static void main(String[] args){
        n=sc.nextInt();
        dfs(1);
        System.out.println(ans);
    } 
}

BFS

Flood Fill

首先,这本质上是一个搜索算法。他的作用是对一个图(目前我用到的都是二维数组的图)的所有的连通块进行操作

它是可以从其中一个点出发,扩展与这个点相邻的所有点

也就是说,给定连通块的所有的点,都可以把这个连通块上所有的点全部搜到

class Point{
	int x;
	int y;
	
	public Point(int x,int y) {
		this.x=x;
		this.y=y;
	}
}
static int dx[]= {0,0,-1,1};	//坐标偏移量数组	
static int dy[]= {1,-1,0,0};
static int vis[][]=new int[N][N];	//标记数组
static Queue<Point> q=new LinkedList<>();	

private static int bfs(Point p) {
		q.offer(p);	//起点进队
		vis[p.x][p.y]=1; //起点算来过了
    
		while(!q.isEmpty()) {
			Point p =q.poll();

			for(int i=0;i<4;i++) {
				int x1=p.x+dx[i];
				int y1=p.y+dy[i];
				
				if(x1<0||y1<0||x1>=n||y1>=m) continue;	//范围不合法
				if(a[x1][y1]==1) continue;	//有障碍物,此路不通
				if(vis[x1][y1]==1) continue;	//已经走过了
				
                vis[x1][y1]=1;	//标记这时来过了
				q.offer(new Point(x1, y1));	 //合法,进队
			}
		}
		return *; //可能要返回的值
	}

并查集

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;

int n, m;
int p[N];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]); // 路径压缩,没找到根节点,就继续找自己的祖宗
    return p[x];
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) p[i] = i; // 自己是自己的祖先

    while (m--) {
        char op;
        int a, b;
        cin >> op >> a >> b;
        if (op == 'M')
            p[find(a)] = find(b); // a家族加入b家族
        else {
            if (find(a) == find(b))	//祖宗一样
                puts("Yes");
            else
                puts("No");
        }
    }
    return 0;
}

树

普通二叉树

已知后序中序求先序遍历

public class 二叉树求先序遍历 {
	
	static String sz,sh;
	static Scanner sc=new Scanner(System.in);
	public static void main(String[] args) {
		sz=sc.next();	
		sh=sc.next();
		dfs(sz,sh);
	}
	
	static void dfs(String sz,String sh) {
			if(sz.length()==0) return;
			char c=sh.charAt(sh.length()-1);
			int idx=sz.indexOf(c);
			System.out.print(c);
			dfs(sz.substring(0,idx), sh.substring(0,idx));
			sh=sh.substring(0,sh.length()-1);
			dfs(sz.substring(idx+1), sh.substring(idx));
	}
}

已知先序中序求后序遍历

public class 二叉树求后序遍历 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String b = scanner.next();	//中序
        String a = scanner.next();	//先序
        int l = a.length() - 1;
        dfs(0, l, 0, l, a, b);
    }

    private static void dfs(int x, int y, int p, int q, String a, String b) {
        if (x > y || p > q) {
            return;
        } else {
            int i = b.indexOf(a.charAt(x));
            dfs(x + 1, x + i - p, p, i - 1, a, b);
            dfs(x + i - p + 1, y, i + 1, q, a, b);
            System.out.print(a.charAt(x));
        }
    }
}

完全二叉树

http://t.csdnimg.cn/baHAD

完全二叉树的层序遍历

package 天梯2024选拔;

import java.util.Scanner;

public class Test {
	static Scanner scanner=new Scanner(System.in);
	static int N=50;
	static int tree[]=new int[N];
	static int n;
	public static void main(String[] args) {
		n=scanner.nextInt();
		midOrder(1);
		for(int i=1;i<n;i++) {
			System.out.print(tree[i]+" ");
		}
		System.out.println(tree[n]);
	}
	
	private static void preOrder(int index) {	//先序遍历输入
		if(index<=n) {
			tree[index]=scanner.nextInt();
			preOrder(index*2);//遍历左子树
			preOrder(index*2+1);//遍历右子树
		}
	}
	
	private static void lastOrder(int index) {	//后序遍历输入
		if(index<=n) {
			lastOrder(index*2);//遍历左子树
			lastOrder(index*2+1);//遍历右子树
			tree[index]=scanner.nextInt();
		}
	}
	
	private static void midOrder(int index) {	//中序遍历输入
		if(index<=n) {
			midOrder(index*2);//遍历左子树
			tree[index]=scanner.nextInt();
			midOrder(index*2+1);//遍历右子树
		}
	}
}

判断是否完全二叉树

http://t.csdnimg.cn/xQDre

#include<bits/stdc++.h>
using namespace std;
int a[100];//利用二叉树结点编号的特点,使用int数组存树 
int mmax=1;
int root;
void insert(int x)
{
	int id=1;
	while(a[id]!=0)//找插入位置
	{
		if(x>a[id]) id=id*2;//左孩子下标
		else id=id*2+1;//右孩子下标
	}
	a[id]=x;
	mmax=max(id,mmax);//更新最大编号 
}
int main()
{
    int n;
    cin>>n;
    cin>>root;
    a[1]=root;//由于题目说将输入的N个正整数'顺序'插入一个初始为空的二叉搜索树,隐含第一个输入的数即是树的根
    for(int i=1;i<n;i++)
    {
    	int x;
    	cin>>x;
    	insert(x);
	}
	cout<<root;
	for(int i=2;i<=mmax;i++)//因为是数组存树,所以遍历数组即是树的层序遍历 
	{
		if(a[i])
		cout<<" "<<a[i];
	}
	//要判断是否完全,看最大编号是否为n就行
	if(mmax==n) cout<<"\nYES";
	else cout<<"\nNO";
    return 0;
}

线段树

图论

image-20240408201445576

dijkstra

复杂度:O((n+m)log2n),在稠密图中有不俗的表现

特征:贪心的思想

注意:求解单源最短路径问题,且没有负权边图时优先用dijkstra

class PII{
	int dist;	//当前点的最短距离
	int u;		//当前点的序号
	public PII(int dist,int u) {
		this.dist=dist;
		this.u=u;
	}
}
public class dijkstra求最短路 {
	static int N=(int) (1e5+10),M=2*N,INF=0x3f3f3f3f;	//INF 10位数
	static int n,m,idx;
	static int e[]=new int[M];	//第i条边指出的点
	static int ne[]=new int[M];	//第i条边指向的下一条边的索引
	static int w[]=new int[M];	//存储第i条边的权值
	static int h[]=new int[N];	//存储每个点第一个邻居的索引
	static int d[]=new int[N];	//最短路径数组
	static boolean st[]=new boolean[N];	//判断当前节点是否在队列中
	static Queue<PII> q=new PriorityQueue<>(new Comparator<PII>() {
		public int compare(PII o1, PII o2) {
			return o1.dist-o2.dist;
		}
	});
	
	public static void main(String[] args) throws IOException {
		Arrays.fill(h, -1);
		n=I();
		m=I();
		while(m-->0) {
			int x=I();
			int y=I();
			int z=I();
			add(x, y, z);
		}
		System.out.println(dijkstra());
	}
	
	static int dijkstra() {
		Arrays.fill(d, INF);
		d[1]=0;
		q.offer(new PII(0,1));	//源点先不用标记
		while(!q.isEmpty()) {
			PII	p=q.poll();
			int dist=p.dist;
			int u=p.u;
			
			if(!st[u]) {//该点不在队列中
				st[u]=true;	//这里记得标记进队列了
				for(int i=h[u];i!=-1;i=ne[i]) {
					int v=e[i];
					if(d[v]>dist+w[i]) {
						d[v]=dist+w[i];
						q.offer(new PII(d[v],v));
					}
				}
			}
		}
		return d[n];
	}
	
	static void add(int a,int b,int c) {
		e[idx]=b;
		ne[idx]=h[a];
		w[idx]=c;
		h[a]=idx++;
	}
}

spfa

复杂度:上限为O(NM)=O(VE)

特征:BFS的思想

注意:若题意为图中没有负权边,则不建议用spfa,因为当n,m过大时,有毒瘤数据导致超时,无法AC

static int N=(int) (1e5+10),M=2*N,INF=0x3f3f3f3f; //注意下面的存储边信息的数组大小得开M
static int n,m,idx;
static int e[]=new int[M];	//第i条边指出的点
static int ne[]=new int[M];	//第i条边指向的下一条边的索引
static int w[]=new int[M];	//存储第i条边的权值
static int h[]=new int[N];	//存储每个点第一个邻居的索引
static int d[]=new int[N];	//最短路径数组
static boolean st[]=new boolean[N];	//判断当前节点是否在队列中
static Queue<Integer> q=new LinkedList<>();
public static void main(String[] args) throws IOException {
    n=I();
    m=I();

    Arrays.fill(h, -1);

    while(m-->0) {
        int a=I();
        int b=I();
        int c=I();
        add(a,b,c);
    }

    spfa(1);

    if(d[n]==INF) System.out.println("impossible");
    else {
        System.out.println(d[n]);
    }

}

private static void spfa(int s) {
    Arrays.fill(d, INF);
    d[s]=0;		//这一步不要忘了,源点到自己的距离为0,不然 d[v]=d[u]+w[i];这一步一直无法更新源点到其他点的距离
    q.add(s);
    st[s]=true;
    while(!q.isEmpty()) {
        int u=q.poll();
        st[u]=false;
        for(int i=h[u];i!=-1;i=ne[i]) {
            int v=e[i];
            if(d[v]>d[u]+w[i]) {
                d[v]=d[u]+w[i];
                if (!st[v]) {	//如果不在队列,就入队
                    q.offer(v);
                    st[v] = true;
                }
            }
        }
    }
}

private static void add(int a, int b, int c) {
    e[idx]=b;
    ne[idx]=h[a];
    w[idx]=c;
    h[a]=idx++;
}

求解多源最短路径问题

floyd

//初始化:
    INF=0x3f3f3f3f  //0x3f3f3f3f的十进制是1061109567,也就是10^9 级别的(和0x7fffffff一个数量级),
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// 算法结束后,d[a][b]表示a到b的最短距离
static void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

DP

背包dp

01背包

二维数组

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int f[N][N];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;
        for (int j = 1; j <= m; j++) {
            f[i][j] = f[i - 1][j];
            if (j >= v)
                f[i][j] = max(f[i][j], f[i - 1][j - v] + w); // 两种情况取最大值
        }
    }

    printf("%d\n", f[n][m]);
    return 0;
}

一维数组(倒着推,滚动数组)

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;

int n, m;
int f[N];

int main() {
    cin >> n >> m;

    // 01背包模板
    for (int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;
        for (int j = m; j >= v; j--)
            f[j] = max(f[j], f[j - v] + w);
    }
    printf("%d\n", f[m]);	//注意,这里输出的是第m项,因为容量最大为m,而不是n项
    return 0;
}

完全背包

二维

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int f[N][N];

int main() {
    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;
        for (int j = 1; j <= m; j++) {
            f[i][j] = f[i - 1][j];
            if (j >= v)
                f[i][j] = max(f[i][j], f[i][j - v] + w);
        }
    }
    printf("%d\n", f[n][m]);
    return 0;
}

一维(正着推,和01背包方向正好相反)

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;
int n, m;
int f[N];

// 完全背包问题
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;
        for (int j = v; j <= m; j++)
            f[j] = max(f[j], f[j - v] + w);
    }
    printf("%d\n", f[m]);
    return 0;
}

为什么01背包要倒着推,完全背包要顺着推 - pechpo - 博客园 (cnblogs.com)

多重背包

二维

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;

int f[N][N];
int main() {
    cin >> n >> m;

    for (int i = 1; i <= n; i++) { // 讨论每个物品
        int w, v, s;
        cin >> v >> w >> s;
        for (int j = 0; j <= m; j++)                   // 讨论每个剩余的体积
            for (int k = 0; k <= s && v * k <= j; k++) // 讨论加入的个数
                f[i][j] = max(f[i][j], f[i - 1][j - k * v] + w * k);
    }
    printf("%d\n", f[n][m]);
    return 0;
}

一维(倒着推,和01背包相似)

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int n, m;
int f[N];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        int v, w, s;
        cin >> v >> w >> s;
        for (int j = m; j >= v; j--)
            for (int k = 0; k <= s && k * v <= j; k++)
                f[j] = max(f[j], f[j - v * k] + w * k);
    }
    printf("%d\n", f[m]);
    return 0;
}
posted @ 2024-06-29 16:03  Leshio  阅读(76)  评论(0)    收藏  举报