A:貪欲アルゴリズム
1.地金ポイント
地金を半分に切断し、銅板の長さと同じ値をとります。例えば、20金の延べ棒の長さは、20銅を費やす必要があり、関係なく、どのくらいの長さの半分に切ります。金地金のブロック全体を分割したい人々のグループは、どの州で最も銅を分割するには?
例えば、3つの合計を表す配列{10、20}、3つの部分10,20,30に分割する= 60バー10 + 20 + 30地金の全体の長さを与え。50に第一のバー10及び60の長さは、60は20及び30の長さに、次いでバー50とを取る場合、総コストは50 110銅を過ごしました。
しかし、第一のバー30と60 30に、60の長さは、10にバー30と20の長さを取る場合は、30が90費やした銅の合計を過ごしました。
入力アレイ、分割されたリターン最小コスト。
/**
* 分金条,求最小代价和,可以使用哈弗曼树来做
*/
public class GoldBullion {
private static class Node implements Comparable<Node> {
public Node(Integer value) {
this.value = value;
}
public Integer value;
public Node parent;
public Node left;
public Node right;
public boolean isLeaf() {
return left == null && right == null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
}
private static PriorityQueue<Node> pq = new PriorityQueue<>();
private static int sum;
//分金条的过程,就是构造哈弗曼树的过程
//当哈弗曼树构造好,根节点就是金条的总长度,叶子节点就是想分出的小金条长度
//叶子节点的路径权重和就是划分所需要的代价
public static int bull(int[] input) {
//初始化多个树
for (int i = 0; i < input.length; i++) {
pq.add(new Node(input[i]));
}
//开始创建
while (pq.size() > 1) {
Node left= pq.poll();
Node right = pq.poll();
Node parent = new Node(left.value+right.value);
parent.left = left;
parent.right = right;
pq.add(parent);
}
Node root = pq.poll();
return count(root,0);
}
//从哈弗曼树求出所有叶子节点的路径权值和
private static int count(Node node,int count){
if (node == null)
return 0;
if (node.isLeaf())
sum += count* node.value;
else
{
if (node.left != null)
count(node.left,count+1);
if (node.right != null)
count(node.right,count+1);
}
return sum;
}
public static void main(String[] args) {
int[] arr = {10,20,30};
System.out.println(bull(arr));
}
}
方法2:
public static int bull2(int[] arr){
PriorityQueue<Integer> queue = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
queue.add(arr[i]);
}
int count = 0;
while (queue.size() > 1)
{
int a = queue.poll();
int b = queue.poll();
int sum = a + b;
queue.add(sum);
count += sum;
}
return count;
}
2.プロジェクトを行います
入力:配列費用のパラメータ1、パラメータ2のn数は、正のパラメータ配列3,4正パラメータk、正の数mの利益を
コスト[i]は、プロジェクトの数を表し、私が取る
[i]はiのコストを控除した後のアイテムの数を表し、利益だけでなく、(利益)お金を稼ぐ
シリアルあなたが唯一の最もk個のアイテムを行うことができないk個の並列
メートルはあなたを表し初期資金
注:次のプロジェクトを行うためにあなたをサポートすることができ、プロジェクトとすぐに利益を終えるたびに。
出力:最大お金は、あなたは最終的に数を取得します。
/**
* 做项目
*/
public class ProjectTest {
//封装数据项
static class Process implements Comparable{
int cost;
int profit;
public Process(int cost, int profit) {
this.cost = cost;
this.profit = profit;
}
@Override
public int compareTo(Object o) {
return this.cost - ((Process)o).cost;
}
}
//实现两个堆,代价的最小堆和收益的最大堆
//我们希望在花费最小代价的情况下获得最大收益
static class CostComparator implements Comparator<Process>{
@Override
public int compare(Process o1, Process o2) {
return o1.cost - o2.cost;
}
}
static class ProfitComparator implements Comparator<Process>{
@Override
public int compare(Process o1, Process o2) {
return o2.profit - o1.profit;
}
}
static PriorityQueue<Process> costQueue = new PriorityQueue<>(new CostComparator());
static PriorityQueue<Process> profitQueue = new PriorityQueue<>(new ProfitComparator());
public static int doProject(int[] costs,int[] profits,int k,int m){
//初始化代价堆
for (int i = 0; i < costs.length; i++) {
Process process = new Process(costs[i],profits[i]);
costQueue.add(process);
}
//在不超出工程步骤的情况下
for (int i = 0; i < k; i++) {
//找出能够接受的项目
while (!costQueue.isEmpty() && costQueue.peek().cost <= m)
profitQueue.add(costQueue.poll());
//在这些项目中找到最大收益
if (profitQueue.isEmpty())
return m;
m += profitQueue.poll().profit;
}
return m;
}
public static void main(String[] args) {
int[]c = new int[]{10,20,100};
int[]p = new int[]{11,10,200};
System.out.println(doProject(c,p,c.length,50));//71
}
}
活動の3組織
いくつかのプロジェクトには、会議室の説教を取り、会議室を説くと同時に、両方のプロジェクトに対応することはできません。プロジェクトと終了時刻を起動するたびに(あなたの具体的なプロジェクトである配列を与えるために)、あなたはスケジュールを手配するために説く、会議室は、説教までのスクリーニングを必要とします。ほとんどの説教のセッションを返します。
public class ActivityTest {
//数据项
static class Activity implements Comparable{
int index;
int start;
int end;
public Activity(int index, int start, int end) {
this.index = index;
this.start = start;
this.end = end;
}
@Override
public String toString() {
return "Activity{" +
"index=" + index +
", start=" + start +
", end=" + end +
'}';
}
@Override
public int compareTo(Object o) {
return this.end - ((Activity)o).end;
}
}
private static PriorityQueue<Activity> endQueue = new PriorityQueue<>();
public static int select(int[] start,int[] end){
Activity[] activities = new Activity[start.length];
for (int i = 0; i < start.length; i++) {
Activity activity = new Activity(i,start[i],end[i]);
activities[i] = activity;
}
endQueue.addAll(Arrays.asList(activities));
//初始化场次
int count = 0;
//选出最早下课时间
while (!endQueue.isEmpty()){
Activity last = endQueue.poll();
count++;
//下一次上课时间应该比上一次上课的时间晚
while (!endQueue.isEmpty() && endQueue.peek().start < last.end)
endQueue.poll();
}
return count;
}
public static void main(String[] args) {
int start[] = {1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12};
int end[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
System.out.println(select(start,end));
}
}
II:再帰
1.求めてn個!結果
//计算阶乘
public static int fac(int n){
if(n <= 1)
return n;
return n*fac(n-1);
}
ハノイの問題の2タワー
HANORは左端右端からプロセス全体を移動させる印刷N層
public static void move(int n,char from, char temp,char to){
//只有一个,直接移动
if(n == 1)
{
System.out.printf("move the %d :%s-->%s%n",n,from,to);
}
else
{
move(n-1,from,to,temp); //把A塔上编号1~n-1的圆盘移动到辅助盘上
System.out.printf("move the %d :%s-->%s%n",n,from,to); // 把最后一个圆盘直接移动到目标盘
move(n-1,temp,from,to); //再把1~n-1的圆盘移到目标盘回来
}
}
3.印刷する文字列
すべてのサブストリングの文字列を印刷
/**
* 打印字符串的所有子串
*/
public class StringTest {
public static void printAllSubString(String s){
if (s == null)
return;
int low = 0;
int high = s.length();
printAllSubString(s,low,high);
}
private static void printAllSubString(String s, int start,int end) {
if (start == s.length())
return;
if (end == s.length() + 1) {
printAllSubString(s,start + 1,start + 2);
}
else {
System.out.printf("%s ", s.substring(start,end));
printAllSubString(s, start, end + 1);
}
}
public static void main(String[] args) {
String s = "abcd";
printAllSubString(s);
}
}
null文字列を含む文字列を印刷する全ての配列は、(すべての組み合わせ)
//全部子序列
public static void printAllSubSequence(String s){
char[] chars = s.toCharArray();
int index = 0;
printAllSubSequence(chars,“”,index);
}
private static void printAllSubSequence(char[] s,String res,int index) {
// if (index == s.length) {
// String temp = String.valueOf(s);
// System.out.printf("%s ", temp);
// return;
// }
if (index == s.length) {
//避免重复
String temp = res.trim();
if (!set.contains(temp))
{
set.add(temp);
System.out.printf("%s ", temp);
}
return;
}
//我想要当前字符
printAllSubSequence(s,res+s[index],index + 1);
//我不想要当前字符
printAllSubSequence(s,res,index + 1);
}
文字列のすべての順列の印刷ではなく、複製配置が必要です
//全排列,每个节点 i 有 n - i 种选择
public static void printAllPermutations(String str) {
char[] chs = str.toCharArray();
printAllPermutations(chs, 0);
}
private static void printAllPermutations(char[] chs, int i) {
//无法与最后的字符交换,打印
if (i == chs.length) {
System.out.printf("%s ",String.valueOf(chs));
}
// 用于保证每次交换的字符不存在重复的字符
HashSet<Character> set1 = new HashSet<>();
//依次与后面的字符交换
for (int j = i; j < chs.length; j++) {
//防止重复
if (!set1.contains(chs[j])){
set1.add(chs[j]);
//我想要第j个字符
swap(chs, i, j);
printAllPermutations(chs, i + 1);
//我不想要第j个字符
swap(chs, i, j);
}
// //我想要第j个字符
// swap(chs, i, j);
// printAllPermutations(chs, i + 1);
// //我不想要第j个字符
// swap(chs, i, j);
}
}
4.牛の問題
牛毎年生まれた牛は、3年後の新しい生まれの牛の成長は、それが死ぬことはないと仮定すると、年間生牛することができます。Nを求めた後、牛の数。
public int noDeath(int i){
if (i <= 0)
return 0;
//因为一头牛需要3年才能生,所以前3年都是不生的,有几头牛就只有几头牛,他们不能再生了
else if (i <= 3)
return i;
//如果超过3年,那么可以先看看上一年剩下的牛,再加上除了近3年生不了的牛,3年前的牛他们还能再生
return noDeath(i-1) + noDeath(i-3);
}
各牛はN、牛の数を求めて、10年生きることができる場合
public static int death(int i,int year){
if (i <= 0)
return 0;
//因为一头牛需要3年才能生,所以前3年都是不生的,有几头牛就只有几头牛,他们不能再生了
else if (i <= 3)
return i;
//如果还未超过死亡期限,则按照上一方法处理
else if (i <= year)
return noDeath(i);
//如果到达死亡期限,可以先看看没死的时候应该有多少牛,再减去死掉的牛个数
return death(i-1,year) + death(i-3,year) - death(i - year,year);
}
5.あなたのスタックを与える、あなたがスタックを逆に、あなただけの再帰関数を使用して、追加的なデータ構造のために適用することはできません。どのように達成するために?
/**
* 逆序栈
*/
public class ReverseStack {
//这个函数递归栈用于存储最后一个元素
public static void reverse(Stack<Integer> stack){
if (stack.isEmpty())
return;
//从栈底到栈顶出栈-->进入辅助栈
int last = getLast(stack);
reverse(stack);
//从栈顶到栈底入栈-->从辅助栈出来
stack.push(last);
}
//这个函数递归栈用于存储其他 元素
//获取栈底
public static int getLast(Stack<Integer> stack){
int res = stack.pop();
//只剩一个,直接返回
if (stack.isEmpty())
return res;
else {
//从栈顶到栈底出栈-->进入辅助栈
int last = getLast(stack);
//把除了最后一个的其他入栈-->离开辅助栈
stack.push(res);
return last;
}
}
}
3:動的計画法(重複排除、ノー後の効果)
- N次元テーブルを決定する可変パラメータの数、
- 可変パラメータの範囲を決定する、範囲決定テーブル
- ターゲットパラメータを決定し、表に記さ
- パラメータの最終的な決意は、判定テーブルにマークすることができます
- 彼は判断することができるすべての値を記入し、国境からフォームに記入し始めました
- 戻る正常な再帰関数に、フォームに記入
1.最小のパスと
2次元配列、数値の各二次元配列が正であるあなたを与える、要件の左上隅から右下隅に移動し、各ステップは、唯一の右または下にすることができます。道に沿って追加する番号に至ります。最小リターンパス。
再帰的なアプローチ
public static int min(int[][] matrix){
return min(matrix,0,0,matrix[0].length - 1,matrix.length - 1);
}
public static int min(int[][] matrix,int x1,int y1,int x2,int y2){
//走到最下面,则向右走
if (y1 > y2)
return min(matrix, x1 + 1, y2, x2, y2);
//走到最右边,则向下走
if (x1 > x2)
return min(matrix, x2, y1 + 1, x2, y2);
//走到终点,返回
if (x1 == x2 && y1 == y2)
return matrix[y2][x2];
//每个点有两个选择:向右尝试,向下尝试
//再取最小值
int temp1 = matrix[y1][x1] + min(matrix, x1 + 1, y1, x2, y2);
int temp2 = matrix[y1][x1] + min(matrix, x1, y1 + 1, x2, y2);
return Math.min(temp1,temp2);
}
動的なプログラミングアプローチ:
//动态规划
public static int minDP(int[][] matrix){
int row = matrix.length - 1;
int col = matrix[0].length - 1;
//缓存表
int[][] dp = new int[row+1][col+1];
//边界值
dp[row][col] = matrix[row][col];
//填充最后一列
for (int i = row - 1; i >= 0; i--) {
dp[i][col] = dp[i + 1][col] + matrix[i][col];
}
//填充最后一行
for (int i = col - 1; i >= 0; i--) {
dp[row][i] = dp[row][i + 1] + matrix[row][i];
}
//填充表格
for (int i = row - 1; i >= 0; i--) {
for (int j = col - 1; j >= 0; j--) {
dp[i][j] = Math.min(dp[i + 1][j],dp[i][j + 1]) + matrix[i][j];
}
}
//目标值
return dp[0][0];
}
2.累積アレイ
アレイARR与えられ、全てのサブアレイに蓄積戻り、最大累積
//递归
public static int max2(int[] arr){
if (arr.length < 1)
return 0;
return max2(arr,0,1);
}
private static int max2(int[] arr, int start,int end) {
if (start == arr.length){
return 0;
}
if (end == arr.length + 1)
return max2(arr,start + 1,start + 2);
int sum = 0;
for (int i = start; i < end; i++) {
sum += arr[i];
}
return Math.max(sum,max2(arr,start,end + 1));
}
//迭代
public static int maxSubArray(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int cur = 0;
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
cur += arr[i];
max = Math.max(cur, max);
if (cur < 0) {
cur = 0;
}
}
return max;
}
//动态规划
public static int max2(int[] arr){
int len = arr.length - 1;
//变化的参数只有一个——索引,建立一维数组
int[] dp = new int[len + 1];
//已知最后一个元素
dp[len] = arr[len];
//开始填表,把相对较大和的数填入
for (int i = len - 1; i >= 0; i--)
dp[i] = Math.max(arr[i] + dp[i + 1],dp[i + 1]);
//目标是第0个元素
return dp[0];
}
蓄積された目的を得ることができない、あなたの整数の目的を与えるには、trueまたはfalseを返します
//递归
public static boolean isSum(int[] arr,int aim){
return isSum(arr,0,0,aim);
}
public static boolean isSum(int[] arr,int index,int sum,int aim){
//从头开始迭代,直接到数组尾部
if (index == arr.length)
return sum == aim;
//选择要当前的数
boolean temp1 = isSum(arr, index + 1, sum + arr[index], aim);
//选择不要当前的数
boolean temp2 = isSum(arr, index + 1, sum, aim);
return temp1 || temp2;
}
//动态规划
private static boolean isSumDP(int[] arr,int aim){
int len = arr.length;
int sum = 0;
for (int i = 0; i < len; i++) {
sum += arr[i];
}
if (sum < aim)
return false;
//根据数组的长度和最大和,建立缓存表
boolean[][] dp = new boolean[len + 1][sum + 1];
//最后一行是确定值,先填充
for (int i = 0; i <= sum; i++) {
dp[len][i] = i == aim;
}
//从最后一行向上,开始填充普通值
for (int i = len - 1; i >= 0; i--) {
for (int j = sum; j >= 0; j--) {
//可以选择当前值或者不选择当前值
if (arr[i] + j <= sum)
dp[i][j] = dp[i + 1][j] || dp[i + 1][arr[i] + j];
//无法选择当前值,因为他之前已经被选了
else
dp[i][j] = dp[i + 1][j];
}
}
return dp[0][0];
}
負の数があります:
private static boolean isSumDP1(int[] arr,int aim){
int len = arr.length;
int min = 0,max = 0;
//统计最小值与最大值
for (int i = 0; i < len; i++) {
if (arr[i] >= 0)
max += arr[i];
else
min += arr[i];
}
if (max < aim || min > aim)
return false;
//根据数组的长度和最大和,建立缓存表
boolean[][] dp = new boolean[len + 1][Math.abs(min) + max + 1];
//最后一行是确定值,先填充
for (int i = 0,j = min; i <= Math.abs(min) + max; i++,j++) {
dp[len][i] = j == aim;
}
//从最后一行向上,开始填充普通值
for (int i = len - 1; i >= 0; i--) {
for (int j = Math.abs(min) + max; j >= 0; j--) {
//可以选择当前值或者不选择当前值
if (arr[i] + j <= Math.abs(min) + max && arr[i] + j >= 0)
dp[i][j] = dp[i + 1][j] || dp[i + 1][arr[i] + j];
//无法选择当前值,因为他之前已经被选了
else
dp[i][j] = dp[i + 1][j];
}
}
//目标是第“(0,0)”个,也就是偏移min个负数后的位置
return dp[0][Math.abs(min)];
}
3.ナップザック問題
[i]はiは、V [i]はアイテムのi番目の値を表すアイテムの重量を表し、wは所定の二つの配列w及びV、二つの配列は、長さが等しいです。固定整数袋を付け、この条件を満たすように最大値を返し、袋を超えてはならない重量を組み合わせたアイテムを選択する必要が、あなたが得ることができます。
//递归
public static int maxValue(int[] w,int[] v,int bag){
return maxValue(w, v, bag,0,0,0);
}
public static int maxValue(int[] w,int[] v,int bag,int index,int weight,int value){
//如果当前重量太重了,或者没有物品了,就不放入,返回当前价值
if (index == w.length || weight + w[index] > bag)
return value;
//选择要当前的数
int yes = maxValue(w, v, bag, index+1,weight + w[index],value + v[index]);
//选择不要当前的数
int no = maxValue(w, v, bag, index+1,weight,value);
//获得最大的价值
return Math.max(yes,no);
}
//动态规划
public static int maxValueDP(int[] w, int[] v, int bag) {
int len = w.length;
//参数变化的有重量和物品的个数。以此建立表,并记录价值
int[][] dp = new int[len + 1][bag + 1];
//最后一
for (int i = 0; i < len; i++) {
dp[i][bag] = v[i];
}
for (int i = len - 1; i >= 0; i--) {
for (int j = bag; j >= 0; j--) {
//可以选择当前值或者不选择当前值
if (w[i] + j <= bag)
dp[i][j] = Math.max(dp[i + 1][j], v[i] + dp[i + 1][w[i] + j]);
else
dp[i][j] = dp[i + 1][j];
}
}
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println();
}
//目标是第“(0,0)”个,也就是偏移min个负数后的位置
return dp[0][0];
}