题目描述
一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn,求旅行者能获得最大总价值。
输入
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);
第2…N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
样例输入
10 4
2 1
3 3
4 5
7 9
样例输出
12
题目解析
声明一个大小为dp[n][m]的二维数组,dp[i][j]表示在要装第i件物品且背包容量为j时的最大价值。
因此,可以分为几种情况讨论:
(1)j
2、如果不拿,dp[i][j]=dp[i-1][j];
需要比较这两种情况哪个能获得更大的价值,即选择Math.max(dp[i-1][j-w[i]]+c[i],dp[i-1][j]);
import java.util.Scanner;
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int m,n;
m=scan.nextInt();//背包容量
n=scan.nextInt();//物品数量
int w[]=new int[m+1];//重量
int c[]=new int[n+1];//价值
for(int i=1;i<=n;i++) {
w[i]=scan.nextInt();
c[i]=scan.nextInt();
}
scan.close();
int dp[][]=new int[n+1][m+1];
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(j>=w[i])
dp[i][j]=Math.max(dp[i-1][j-w[i]]+c[i],dp[i-1][j]);
else
dp[i][j]=dp[i-1][j];
}
}
System.out.println(dp[n][m]);
}
}
题目描述
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入
第一行为一个整数,表示箱子容量;
第二行为一个整数,表示有n个物品;
接下来n行,每行一个整数表示这n个物品的各自体积。
输出
一个整数,表示箱子剩余空间。
样例输入
24
6
8
3
12
7
9
7
样例输出
0
import java.util.Scanner;
//装箱问题
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int v=scan.nextInt();//箱子容量
int n=scan.nextInt();//n个物品
int num[]=new int[n+1];//n个物品各自体积
for(int i=1;i<=n;i++)
num[i]=scan.nextInt();
scan.close();
int res=1000000;
int dp[][]=new int [n+1][v+1];
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
if(j>=num[i])
dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-num[i]]+num[i]);
else
dp[i][j]=dp[i-1][j];
}
if(res>v-dp[i][v])
res=v-dp[i][v];
}
System.out.println(res);
}
}
题目描述
设有N*N的方格图(N< =10),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
某人从图的左上角的A 点(1,1)出发,可以向下行走,也可以向右走,直到到达右下角的B点(N,N)。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
输入
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
输出
只需输出一个整数,表示2条路径上取得的最大的和。
样例输入
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
样例输出
67
题目解析
四维dp。假设有两个人在方格中走,dp[i][j][k][l]表示当第一个人走到坐标(i,j),第二个人走到坐标(k,l)时取得的最大值的和。一个人走到(i,j)有两种方式,一种是向下行走,从(i,j-1)走到的;另一种是向右行走,从(i-1,j)走到的。因此两个人就拥有四种情况,要取这四种情况中的最大值。
注意:当两个人同时走到一个格子时,格子的值只能加一次。
import java.util.Scanner;
//方格取数
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();//方格大小
int square[][]=new int [n+1][n+1];
int a,b,c;
while(true) {
a=scan.nextInt();
b=scan.nextInt();
c=scan.nextInt();
if(a==0 && b==0 && c==0)
break;
else
square[a][b]=c;
}
scan.close();
int dp[][][][]=new int[n+1][n+1][n+1][n+1];
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
for(int k=1;k<=n;k++) {
for(int l=1;l<=n;l++) {
//两个人的四种运动情况
int t1=Math.max(dp[i-1][j][k-1][l], dp[i-1][j][k][l-1]);
int t2=Math.max(dp[i][j-1][k][l-1], dp[i][j-1][k-1][l]);
int t3=Math.max(t1, t2);
if(i!=k && j!=l)//如果坐标不相等,即两人不在同一位置,则都加进去
dp[i][j][k][l]=t3+square[i][j]+square[k][l];
else//如果坐标相等,即两人在同一位置,则只加一次
dp[i][j][k][l]=t3+square[i][j];
}
}
}
}
System.out.println(dp[n][n][n][n]);//两人都走到最后一个方格时的值即为最大值
}
}
题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。 从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然 数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。 现在,请你帮助小渊和小轩找到这样的两条路径。
数据规模和约定
100%的数据满足:1< =m,n< =50
输入
输入第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1< =m,n< =50)。
接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。
输出
输出一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
样例输入
3 3
0 3 9
2 8 5
5 7 0
样例输出
34
import java.util.Scanner;
//传纸条
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int m=scan.nextInt();
int n=scan.nextInt();
int num[][]=new int[m+1][n+1];
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
num[i][j]=scan.nextInt();
}
}
scan.close();
int dp[][][][]=new int[m+1][n+1][m+1][n+1];//dp[i][j][k][l]表示一个人传到i,j,另一个人传到k,l时取得的最大值的和
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
for(int k=m;k>=1;k--) {
for(int l=n;l>=1;l--) {
int t1=Math.max(dp[i-1][j][k-1][l], dp[i][j-1][k][l-1]);
int t2=Math.max(dp[i-1][j][k][l-1], dp[i][j-1][k-1][l]);
int t3=Math.max(t1, t2);
if(i!=k && j!=l)
dp[i][j][k][l]=t3+num[i][j]+num[k][l];
else
dp[i][j][k][l]=t3+num[i][j];
}
}
}
}
System.out.println(dp[m][n][m][n]);
}
}
题目描述
一个正整数可以划分为多个正整数的和,比如n=3时:
3;1+2;1+1+1;
共有三种划分方法。
给出一个正整数,问有多少种划分方法。
数据规模和约定
n< =100
输入
一个正整数n
输出
一个正整数,表示划分方案数
样例输入
3
样例输出
3
题目解析
do[i][j]表示对数字i进行不大于j的拆分,一共有多少种拆分方法。
首先初始化1、dp[i][1]=1,把这个数划分为不大于1的方法只有一种,即拆分的每一项都为1;
2、dp[0][j]=1,把0划分的方法只有一种。
当i>=j时,dp[i][j]=dp[i][j-1]+dp[i-j][j];其中dp[i][j-1]表示划分的每一个数都小于j,dp[i-j][j]表示划分后至少有一个数字等于
当j>i时,dp[i][j]=dp[i][j];划分方法就是将数字划分为不大于本身。
import java.util.Scanner;
//数的划分
public class Main {
private static int MAX = 101;
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
scan.close();
int dp[][]=new int[MAX][MAX];
for(int i=0;i<=n;i++) {
dp[i][1]=1;
dp[0][i]=1;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(i>=j)
dp[i][j]=dp[i][j-1]+dp[i-j][j];
else
dp[i][j]=dp[i][i];
}
}
System.out.println(dp[n][n]);
}
}
题目描述
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到小蛮手里。两种传球的方法被视作不同的方 法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并假设小蛮为1号,球传了3次回到小蛮手里的方 式有1-> 2-> 3-> 1和1-> 3-> 2-> 1,共2种。
数据规模和约定
100%的数据满足:3< =n< =30,1< =m< =30
输入
共一行,有两个用空格隔开的整数n,m(3< =n< =30,1< =m< =30)。
输出
t共一行,有一个整数,表示符合题意的方法数。
样例输入
3 3
样例输出
2
题目解析
dp[i][j]表示传i次球,传给第j个人的方法数。
(1)dp[1][n]=1 表示传一次球,传到第n个人,有一种方法
dp[1][2]=1 表示传一次球,传到第2个人,有一种方法
dp[0][1]=1 表示自己传给自己
(2)1、如果 j= =1,即传给第一个人时,有两种情况,可能是第2个人传来的,也可能是第n个人传来的,因此dp[i][j]=dp[i-1][n]+dp[i-1][2];
2、如果j==n,即传给第n个人时,有两种情况,可能是第1个人传来的,也可能是第n-1个人传来的,因此dp[i][j]=dp[i-1][1]+dp[i-1][n-1];
3、一般情况下也有两种可能,分别是左右两边传来的,因此dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];
import java.util.Scanner;
//传球游戏
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();//n个同学
int m=scan.nextInt();//传m次球
scan.close();
int dp[][]=new int [m+1][n+1];//dp[i][j]表示传i次球,传到第j个人的方法数
dp[1][n]=1;//传一次球,传到第n个人,有一种方法
dp[1][2]=1;//传一次球,传到第2个人,有一种方法
dp[0][1]=1;//自己传给自己
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
if(j==1)
dp[i][j]=dp[i-1][n]+dp[i-1][2];
else if(j==n)
dp[i][j]=dp[i-1][1]+dp[i-1][n-1];
else
dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];
}
}
System.out.println(dp[m][1]);
}
}
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界 上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子, 这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明 的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
数据规模和约定
对于全部的数据,M < = 100。
输入
第一行有两个整数T(1 < = T < = 1000)和M(1 < = M < = 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整 数,分别表示采摘某株草药的时间和这株草药的价值。
输出
包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例输入
70 3
71 100
69 1
1 2
样例输出
3
import java.util.Scanner;
//入学考试
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int t=scan.nextInt();//用来采草药的时间
int m=scan.nextInt();//草药的数目
int time[]=new int[m+1];//采到这株草药用的时间
int value[]=new int[m+1];//这株草药的价值
for(int i=1;i<=m;i++) {
time[i]=scan.nextInt();
value[i]=scan.nextInt();
}
scan.close();
int dp[][]=new int [m+1][t+1];
for(int i=1;i<=m;i++) {
for(int j=1;j<=t;j++) {
if(j>=time[i])
dp[i][j]=Math.max(dp[i-1][j-time[i]]+value[i], dp[i-1][j]);
else
dp[i][j]=dp[i-1][j];
}
}
System.out.println(dp[m][t]);
}
}
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:
v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]w[jk]。(其中为乘号)
请你帮助金明设计一个满足要求的购物单。
输入
输入第1行,为两个正整数,用一个空格隔开:
N m
(其中N(<30000)表示总钱数,m(<25)为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数
v p
(其中v表示该物品的价格(v<=10000),p表示该物品的重要度(1~5))
输出
输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
样例输入
1000 5
800 2
400 5
300 5
400 3
200 2
样例输出
3900
import java.util.Scanner;
//开心的金明
public class Main {
public static void main(String[]args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();//总钱数
int m=scan.nextInt();//希望购买的物品个数
int v[]=new int[m+1];//物品的价格
int w[]=new int[m+1];//物品的重要度
for(int i=1;i<=m;i++) {
v[i]=scan.nextInt();
w[i]=scan.nextInt();
}
scan.close();
int dp[][]=new int[m+1][N+1];
for(int i=1;i<=m;i++) {
for(int j=1;j<=N;j++) {
if(j>v[i])
dp[i][j]=Math.max(dp[i-1][j-v[i]]+w[i]*v[i], dp[i-1][j]);
else
dp[i][j]=dp[i-1][j];
}
}
System.out.println(dp[m][N]);
}
}