第七部 贪心和动归

问题列表

  1. 硬币问题
  2. 渡河问题
  3. 01背包问题

二、渡河问题

代码块+问题描述

/*
描述

一群N人希望只乘一条船穿过一条河,最多只能载两个人。因此,必须安排某种穿梭装置以便来回划船,以便所有人都能穿过。
每个人都有不同的划船速度;一对夫妇的速度取决于较慢者的速度。您的工作是确定使这些人相处的时间最短的策略。
输入值

输入的第一行包含一个整数T(1 <= T <= 20),即测试用例的数量。然后是T例。每个案例的第一行包含N,第二行包含N个整数,给出每个人过河的时间。每个案例前面都有一个空白行。人数不会超过1000,而且没有人花费超过100秒的时间穿越。
输出量

对于每个测试用例,打印一行,其中包含所有N个人穿过河流所需的总秒数。
样本输入

1
2
4
1 2 5 10
样本输出

17
 */
import java.util.Arrays;
import java.util.Scanner;
public class 渡河问题P {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		int T = sc.nextInt();
		int N = sc.nextInt();
		int p[] = new int[N];
		for(int i = 0; i < N; i++){
			p[i] = sc.nextInt();
		}
		Arrays.sort(p);
		
		int ans = 0;
		for(int i = 0; i < T; i++){
			ans += f(p, N);
		}
		
		//打印最后花费的时间
		System.out.println(ans);
	}
	
	//使用贪心算法,算出最短的渡河时间
	static int f(int p[], int n){
		int ans = 0;
		while(n > 0){
			//最后一个人,用最快的那个人渡
			if(n == 1){
				ans += p[0];
				break;
			//最后两个人,用次快的那个人渡
			}else if(n == 2){
				ans += p[1];
				break;
			//最后三个人,用最快的那三个人来渡
			}else if(n == 3){
				ans += p[0]+p[1]+p[2];
				break;
			//如果还有大于3的人数没有完成渡河,那么判断两种情况
			}else{
				//情况一:用速度最快的那个人带全部人
				int s1 = p[0]+p[n-1] + p[0]+p[n-2];
				//情况二:把最快的两个人先渡河,然后放回较慢的那个人来载后面的人渡河
				int s2 = p[0]+p[1] + p[1]+p[n-1];
				//使用ans存储每次所花费的最短渡河时间
				ans += Math.min(s1, s2);
				//循环一次,渡河一回,人数减二
				n-=2;
			}
		}
		
		return ans;
	}
}

三、背包问题

代码块1(递归求解)

/*
问题描述:
有n个重量分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,
求所有挑选方案中价值总和的最大值。
1 <= n <= 100, 1 <= W <= 10000;

输入:
n = 4
(w, v) = {(2,3), (1,2), (3,4), (2,2)}
W = 5
输出:
7(选择第0、1、3号物品)

因为对每个物品只有选和不选两种情况,所以这个问题称为01背包
 */
public class 背包问题01递归 {
	static int w[] = {2, 1, 3, 2};//重量表
	static int v[] = {3, 2, 4, 2};//价值表
	static int n = 4;//物品数量
	static int W = 5;//背包的承重极限
	public static void main(String[] args) {
		
		int ww = W;//拷贝一份背包的承重极限
		int ans = dfs(0, ww);
		System.out.println(ans);
	}
	
	static int dfs(int i, int ww){
		if(i>=n || ww<=0)return 0;
		
		int v2 = dfs(i+1, ww);
		if(ww >= w[i]){
			int v1 = v[i] + dfs(i+1,ww-w[i]);
			return Math.max(v1, v2);
		}else{
			return v2;
		}
	}

}

代码块2(动归求解)

//计算之前先查询,计算之后做记录
//重叠子问题不重复求解
/*
问题描述:
有n个重量分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,
求所有挑选方案中价值总和的最大值。
1 <= n <= 100, 1 <= W <= 10000;

输入:
n = 4
(w, v) = {(2,3), (1,2), (3,4), (2,2)}
W = 5
输出:
7(选择第0、1、3号物品)

因为对每个物品只有选和不选两种情况,所以这个问题称为01背包
 */
import java.util.Arrays;
public class 背包问题01动归 {
	static int w[] = {2, 1, 3, 2};//重量表
	static int v[] = {3, 2, 4, 2};//价值表
	static int n = 4;//物品数量
	static int W = 5;//背包的承重极限
	public static void main(String[] args) {
		
		int ww = W;
		//横坐标n:物品总数;纵坐标W+1:背包的承重极限
		res = new int[n][W+1];
		//将二维数组res中的数据全部初始化为-1
		for(int i = 0; i < n; i++){
			Arrays.fill(res[i],-1);
		}
		
		int ans = dfs(0, ww);
		System.out.println(ans);
	}
	
	static int[][]res;
	static int dfs(int i, int ww){
		if(i>=n || ww<=0)return 0;
		//1.计算之前做查询
		if(res[i][ww] >= 0){
			return res[i][W];
		}
		
		int v2 = dfs(i+1, ww);
		int ans;
		if(ww >= w[i]){
			int v1 = v[i] + dfs(i+1,ww-w[i]);
			ans = Math.max(v1, v2);
		}else{
			ans = v2;
		}
		//2.计算之后做记录
		res[i][ww] = ans;
		return ans;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值