常用算法模板
常用数据结构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);
}
}
前缀和


#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;
}
差分
特征:
区间修改,单点查询
但是无论修改多少次,只有当所有修改操作结束了,才能查询(求前缀和),而且是单点查询

#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标记
} //因此这里让合数始终被最小的质数标记,
}
}
分解质因数
以下这道题有模板,需要记住!!并且有数学定理推论———唯一分解定理
牛客周赛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);
}
}
这道题用传统的数组来做不行,会超过数组最大声明长度
快速幂

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));
}
}
}
完全二叉树
完全二叉树的层序遍历
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);//遍历右子树
}
}
}
判断是否完全二叉树
#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;
}
线段树
图论

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;
}
多重背包
二维
#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;
}

浙公网安备 33010602011771号