publicclassQ1{publicstaticint[] w ={2,1,3,2};//物品重量publicstaticint[] v ={3,2,4,2};//物品价值publicstaticint n =4;//物品数量publicstaticint W =5;//背包容量publicstaticint[][] rec;//用一张表维护状态publicstaticvoidmain(String[] args){
rec =newint[n][W+1];//将每一个值都先填为-1for(int i =0; i < rec.length; i++){
Arrays.fill(rec[i],-1);}
ww = W;
ans =m(0, ww);
System.out.println(ans);}publicstaticintm(int i,int ww){if(ww <=0)return0;if(i == n)return0;//先查表if(rec[i][ww]>=0){return rec[i][ww];}int v2 =m(i+1, ww);//不选int ans;if(ww >= w[i]){int v1 = v[i]+m(i+1, ww-w[i]);//选
ans =max(v1, v2);}else{
ans = v2;}//记录
rec[i][ww]= ans;return ans;}}/*
[-1, -1, -1, -1, -1, 7]
[-1, -1, -1, 4, -1, 6]
[-1, -1, 2, 4, 4, 6]
[-1, 0, 2, 2, 2, 2]
*/
DP思想去解决
publicclassQ1{publicstaticint[] w ={2,1,3,2};//物品重量publicstaticint[] v ={3,2,4,2};//物品价值publicstaticint n =4;//物品数量publicstaticint W =5;//背包容量publicstaticvoidmain(String[] args){
System.out.println(dp());}publicstaticintdp(){int[][] dp =newint[n][W+1];//第0列全是0for(int i =0; i < dp.length; i++){
dp[i][0]=0;}//初始化第0行for(int i =0; i < dp[0].length; i++){if(i >= w[0]){
dp[0][i]= v[0];}else{
dp[0][i]=0;}}//从第1行开始for(int i =1; i < dp.length; i++){//从第一列开始,j代表列,也是背包的剩余容量for(int j =1; j < dp[0].length; j++){//如果能拿当前的物品if(j >= w[i]){int v1 = v[i]+ dp[i-1][j-w[i]];//选了当前物品,则需要去dp表找剩余容量可容纳的值int v2 = dp[i-1][j];//不选当前物品,直接是上一行的数字
dp[i][j]=max(v1, v2);}else{//当前物品直接大于了剩余容量,拿不起该物品
dp[i][j]= dp[i-1][j];}}}return dp[dp.length-1][dp[0].length-1];}}/*
[0, 0, 3, 3, 3, 3]
[0, 2, 3, 5, 5, 5]
[0, 2, 3, 5, 6, 7]
[0, 2, 3, 5, 6, 7]
*/
2.3 钢条切割问题
不同长度的钢条的售价不同,现有一根长度为n的钢条和一个价格表,求怎么切能卖出最多钱
递归实现
publicclassQ2{publicstaticint[] price ={1,5,8,16,10,17,17,20,34,30};publicstaticint length =10;publicstaticvoidmain(String[] args){
System.out.println(r(length));}privatestaticintr(int x){if(x ==0)return0;int ans =0;for(int i =1; i <= x; i++){int v = price[i-1]+r(x-i);
ans = Math.max(v, ans);}return ans;}}
用一个表记录
publicclassQ2{publicstaticint[] price ={1,5,8,16,10,17,17,20,34,30};publicstaticint length =10;publicstaticint[] p =newint[length+1];publicstaticvoidmain(String[] args){
System.out.println(r(length));}privatestaticintr(int x){if(x ==0)return0;int ans =0;
Arrays.fill(p,-1);for(int i =1; i <= x; i++){if(p[x-i]==-1){
p[x-i]=r(x-i);}int v = price[i-1]+ p[x-i];
ans = Math.max(v, ans);}
p[x]= ans;return ans;}}
dp解决
publicclassQ2{publicstaticint[] price ={1,5,8,16,10,17,17,20,34,30};publicstaticint length =10;publicstaticint[] vs =newint[length+1];//每一个长度的最佳切割方案publicstaticvoidmain(String[] args){
System.out.println(dp());}publicstaticintdp(){
vs[0]=0;for(int i =1; i <= length; i++){//拥有钢条的长度for(int j =1; j <= i; j++){//保留j为整段//最佳方案要么不切,要么切成其他整数段
vs[i]= Math.max(price[j-1]+vs[i-j], vs[i]);}}
System.out.println(Arrays.toString(vs));return vs[length];}}
publicclassQ3{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);int n = scanner.nextInt();int[] s =newint[n];int[] t =newint[n];for(int i =0; i < n; i++){
s[i]= scanner.nextInt();}for(int i =0; i < n; i++){
t[i]= scanner.nextInt();}
Job[] jobs =newJob[n];for(int i =0; i < n; i++){
jobs[i]=newJob(s[i], t[i]);}//根据结束时间排序
Arrays.sort(jobs);
System.out.println(f(n, jobs));}privatestaticintf(int n, Job[] jobs){int cnt =1;//最早结束的项目int job = jobs[0].t;for(int i =0; i < n; i++){//开始时间比上一个项目结束时间晚的项目,//由于已经排过序,所以找到的第一个一定是符合条件的项目中,结束时间最早的if(jobs[i].s > job){
cnt++;
job = jobs[i].t;}}return cnt;}//通过一个内部类来绑定开始时间和结束时间privatestaticclassJobimplementsComparable<Job>{publicint s;publicint t;publicJob(int s,int t){this.s = s;this.t = t;}@OverridepublicintcompareTo(Job o){return(this.t - o.t ==0)?(this.s - o.s):(this.t - o.t);}}}
2.4 区间选点问题
题目描述
有若干时间区间,求出最少需要几个点可以覆盖全部区间
策略
按结束时间排序,然后选结束时间早的,如果某个结束时间在另一个区间内,跳过该区间
代码实现
publicclassQ4{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);int n = scanner.nextInt();int[] start =newint[n];int[] end =newint[n];for(int i =0; i < n; i++){
start[i]= scanner.nextInt();}for(int i =0; i < n; i++){
end[i]= scanner.nextInt();}
Time[] times =newTime[n];for(int i =0; i < n; i++){
times[i]=newTime(start[i], end[i]);}
Arrays.sort(times);int count =1;for(int i =1; i < n; i++){if(times[i-1].end >= times[i].start && times[i-1].end <= times[i].end){
i++;}
count++;}
System.out.println(count);}publicstaticclassTimeimplementsComparable<Time>{publicint start;publicint end;publicTime(int start,int end){this.start = start;this.end = end;}@OverridepublicintcompareTo(Time o){return(this.end - o.end ==0)?(this.start - o.start):this.end - o.end;}}}
2.5 区间选点加强版
题目描述
有n个区间,每个区间上必须要有m个点,求最少需要多少个点满足条件
策略
按结束时间排序,选最后一个开始,每个区间要先判断已选过的点数
代码实现
publicclassQ5{publicstaticvoidmain(String[] args){/*Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
Time[] times = new Time[n];
for (int i = 0; i < n; i++) {
int start = scanner.nextInt();
int end = scanner.nextInt();
int num = scanner.nextInt();
times[i] = new Time(start, end, num);
}*/int n =5;
Time[] times =newTime[n];
times[0]=newTime(3,7,3);
times[1]=newTime(8,10,3);
times[2]=newTime(6,8,1);
times[3]=newTime(1,3,1);
times[4]=newTime(10,11,1);
Arrays.sort(times);
ArrayList<Integer> list =newArrayList<>();int max = times[n-1].end;int[] axis =newint[max+1];//标记数轴上的点是否被选中for(int i =0; i < n; i++){int s = times[i].start;//起点int t = times[i].end;//终点int cnt =sum(axis, s, t);//找到这个区间已经选点的数量
times[i].num -= cnt;//需要新增的点的数量while(times[i].num >0){if(axis[t]==0){//从区间终点开始选值
axis[t]=1;//将该点置为1,表示已选中
times[i].num--;//需要标记的点变少一个
t--;}else{//这个点是1,本来就选值过
t--;}}}
System.out.println(sum(axis,0, max));
System.out.println(Arrays.toString(axis));}//统计数轴s-t上已经有的点数,因为只有0和1,统计1的个数只需要全部就相加privatestaticintsum(int[] axis,int s,int t){int sum =0;for(int i = s; i < t; i++){
sum += axis[i];}return sum;}publicstaticclassTimeimplementsComparable<Time>{publicint start;publicint end;publicint num;publicTime(int start,int end,int num){this.start = start;this.end = end;this.num = num;}@OverridepublicintcompareTo(Time o){return(this.end - o.end ==0)?(this.start - o.start):this.end - o.end;}}}
2.6 区间覆盖问题
/*
区间覆盖问题
输入:
数字n,表示有几个时段,数字t,表示需要覆盖[1-t]时间段
n个数组,一个数组2个数字,表示开始与结束时间
输出:
最少选几个时间段可以覆盖[1-t]
* */publicclassQ6{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);int n = scanner.nextInt();int m =scanner.nextInt();
Job[] jobs =newJob[n];for(int i =0; i < n; i++){int start = scanner.nextInt();int end = scanner.nextInt();
jobs[i]=newJob(start, end);}//按起点排序
Arrays.sort(jobs);int start =1;//初始化起点int end =1;//初始化终点int ans =1;for(int i =0; i < n; i++){int s = jobs[i].start;int t = jobs[i].end;//最开始的时间的起点>0,不可能覆盖完全了if(i ==0&& s >1){break;}//找起点不大于start的区间中,end最大的if(s <= start){
end =max(t, end);}else{
ans++;
start = end +1;if(s <= start){
end =max(t, end);}else{break;}}if(end >= m){break;}}if(end < m){
System.out.println(-1);}else{
System.out.println(ans);}}publicstaticclassJobimplementsComparable<Job>{publicint start;publicint end;publicJob(int start,int end){this.start = start;this.end = end;}//按区间的起点来排序@OverridepublicintcompareTo(Job o){return(this.start-o.start==0)?(this.end-o.end):(this.start-o.start);}}}
2.7 字典序最小问题
/*
输入一个长度为N字符串S,构造一个长度也为N的字符串T;
起初,T是空串,随后反复进行下列任意操作:
1. 从S的头部删除一个元素加到T的尾部
2. 从S的尾部删除一个元素加到T的头部
目标是要求最后生成的T字典序最小
* */publicclassQ7{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);
String s = scanner.nextLine();
String f =f(s);
System.out.println(f);}privatestatic String f(String s){
String s1 =newStringBuilder(s).reverse().toString();int N = s.length();
StringBuilder res =newStringBuilder();while(res.length()< N){if(s.compareTo(s1)<=0){
res.append(s.charAt(0));
s = s.substring(1);}else{
res.append(s1.charAt(0));
s1 = s1.substring(1);}}return res.toString();}}
2.8 背包问题
求容量为m的背包最多能装几件物品
/*
有n个物品,重量分别为wi,先有一个容量为m的背包
求最多可以装几件物品
* */publicclassQ8{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);
String[] strings = scanner.nextLine().split(" ");int[] w =newint[strings.length];for(int i =0; i < strings.length; i++){
w[i]= Integer.parseInt(strings[i]);}int m = scanner.nextInt();int count =0;int sum =0;
Arrays.sort(w);for(int i =0; i < w.length; i++){if(sum + w[i]<= m){
count++;
sum += w[i];}else{break;}}
System.out.println(count);}}
尽量让装进背包的物品价值最高
/*
n个物品,重量为wi,价值为vi,背包总容量为c
每个物体可以只取走一部分,价值和重量按比例计算
求能装进背包的最大总价值
(因为可以只取走一部分,所有总重量一定为c)
* */publicclassQ9{publicstaticvoidmain(String[] args){
Scanner scanner =newScanner(System.in);int n = scanner.nextInt();//物品的总数double[] w =newdouble[n];//每个物品的重量double[] v =newdouble[n];//每个物品对应的价值
Good[] goods =newGood[n];for(int i =0; i < n; i++){
w[i]= scanner.nextDouble();}for(int i =0; i < n; i++){
v[i]= scanner.nextDouble();}for(int i =0; i < n; i++){
goods[i]=newGood(w[i], v[i]);}//按单位价格排序
Arrays.sort(goods);/*for (Good good : goods) {
System.out.println(good);
}*/double weight = scanner.nextDouble();double remain = weight;double price =0;for(int i =0; i < n; i++){if(remain ==0){break;}if(remain >= goods[i].weight){
remain = remain - goods[i].weight;
price = price + goods[i].value;}else{
price += remain*goods[i].price;
remain =0;}}
System.out.println(price);}publicstaticclassGoodimplementsComparable<Good>{publicdouble weight;publicdouble value;publicdouble price;publicGood(double weight,double value){this.weight = weight;this.value = value;this.price = value / weight;}@OverridepublicintcompareTo(Good o){if(this.price > o.price){return-1;}elseif(this.price < o.price){return1;}else{if(this.weight > o.weight){return-1;}}return0;}@Overridepublic String toString(){return"Good{"+"weight="+ weight +", value="+ value +", price="+ price +'}';}}}
2.9 乘船问题
/*
乘船问题,有n个人,体重为wi;
现又无数个载重为k的船,每个船最多只能坐2个人
问最少几辆船可以让所有人过河
* */publicclassQ10{publicstaticvoidmain(String[] args){
Random random =newRandom();int n =10;int[] weight =newint[n];for(int i =0; i < n; i++){
weight[i]= random.nextInt(40)+80;}/*int[] weight = {88,89,103,106,107,109,110,112,115,119};
int n = weight.length;*/
Arrays.sort(weight);
System.out.println(Arrays.toString(weight));int k =200;//船的载重int left =0;int right = n-1;int count =0;while(left <= right){if(weight[left]+ weight[right]<= k){
count++;
System.out.println(weight[left]+"+"+ weight[right]+"="+(weight[left]+ weight[right]));
left++;
right--;}else{
count++;
System.out.println(weight[right]);
right--;}}
System.out.println(count);}}