其实这道题我也没有完全的弄明白,糊里糊涂就ac了
大致题意:
某公司要建立一套通信系统,该通信系统需要n种设备,而每种设备分别可以有m1、m2、m3、...、mn个厂家提供生产,而每个厂家生产的同种设备都会存在两个方面的差别:带宽bandwidths 和 价格prices。
现在每种设备都各需要1个,考虑到性价比问题,要求所挑选出来的n件设备,要使得B/P最大。
其中B为这n件设备的带宽的最小值,P为这n件设备的总价。
解题思路:
首先需要明确,要使得B/P最大,自然是要令B尽可能大,P尽可能小。
由于B和P是两个相互制约的变量,而他们又要同时取得尽可能地取极值,那么可以先令其中一个值“暂时固定”下来。
令输入的行数就是设备的种类数,即第i行描述的是第i种设备,及提供第i种设备的厂家的产品信息。
枚举+剪枝的做法:
首先B的值肯定是厂家生产的所有设备中的某个带宽值,所以可以枚举所有的带宽,每次枚举B值时,B值就被“暂时固定”了。
其次,记录所选取的B是属于第k种设备的,再从余下的设备中,选取其余n-1种设备各一个,要求所选取的设备的带宽>=B(这是由题意确定的),而价格是要满足带宽的要求下的最小值。
当n种设备都选取后,计算B/P,然后再枚举下一个B,重复上述操作。比较不同的B值所得到的B/P值,选取最大的一个就是答案。
剪枝法:
准备工作:
1、输入时先记录:对于每种设备,厂家所提供的最大带宽MaxB[]
2、对所有设备(无论是否同种类)进行升序快排,以带宽为第一关键字,价格为第二关键字,设备所属的种类编号(1~n)为第三关键字。排序后存放在一维数组dev[]
剪枝:
1、 从小到大枚举dev[]中各个设备的带宽作为B值,设总设备数位m,则从1枚举到m-(n-1)。这是因为至少需要选择从枚举位置开始后面的n种设备,m-(n-1)是上限值,即恰好最后n件设备分别为n种设备。
2、 枚举B值的过程中,若发现B值比某种设备的最大带宽更大,则无需继续枚举,输出前面得到的B/P值。这是因为B是所有设备的最小带宽,若出现某个设备的最大带宽比B小,则说明B的选择是不合法的,又dev[]已按B升序排序,后面的B值更大,也不可能成立,因此无需继续枚举。
3、 枚举B值过程中,对于每个B值,在选择其他设备时要记录选取不同种类的设备个数count。最后当count<n时,说明B值位置往后剩余的设备中已无法提供n-1种不同设备,可直接跳出枚举。
剪枝2比较难懂,再稍微解释一下,以给的数据为例:
1 3
3 100 25 150 35 80 25
2 120 80 155 40
2 100 100 120 110
把该组数据升序排序,得到:
B: 80 100 100 120 120 150 155
P: 25 25 100 80 110 35 40
Id: 1 1 3 2 3 1 2
这样当B枚举到150的时候,即B=150,第三个供应商的所有设备都小于150,取任何一个设备都会导致B<150,矛盾。当然大于150的更不用枚举了,直接剪掉。
package com.njupt.acm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 关于题目中的sample input
* 1 3
* 3 100 25 150 35 80 25
* 2 120 80 155 40
* 2 100 100 120 110
*
* 其实应该有点问题,测试应该按一下输入:
* 1 (测试用例数)
* 3 (供应商个数)
* 3 100 25 150 35 80 25 (第一个供应商提供的设备的情况)
* 2 120 80 155 40 (第二个供应商提供的设备的情况)
* 2 100 100 120 110 (第三个供应商提供的设备的情况)
* @author Administrator
*
*/
public class POJ1018 {
double bp = 0;
public POJ1018() throws NumberFormatException, IOException {
BufferedReader read = new BufferedReader(new InputStreamReader(
System.in));
//记录测试用例的个数
int t = Integer.parseInt(read.readLine());
int num;
int[][] b;
int[][] p;
int[] n;
int bmin;
int[] bmaxs;//保存最大带宽
int bmax;
String[] s;
int sum;
int temp;
for (int i = 0; i < t; i++) {
//num. 供应商的个数
num = Integer.parseInt(read.readLine());
//保存设备的带宽
b = new int[num][];
//保存设备的价格
p = new int[num][];
n = new int[num];
bmin = Integer.MAX_VALUE;
bmaxs = new int[num];//默认值全为0
bmax = Integer.MAX_VALUE;
for (int j = 0; j < num; j++) {
//将每个供应商提供的设备的情况都保存到s数组里面
s = read.readLine().split(" ");
//用n数组来保存某个供应商提供的设备的种类数
n[j] = Integer.parseInt(s[0]);
//用b[]来保存某个供应商提供的各种设备的带宽
b[j] = new int[n[j]];
//用来保存某个供应商提供的设备的价格
p[j] = new int[n[j]];
for (int k = 0; k < n[j]; k++) {
//k * 2 + 1 , 获得某个供应商提供的某种设备的带宽
b[j][k] = Integer.parseInt(s[k * 2 + 1]);
/**
* if···语句的主要逻辑:
* 获得某个供应商所能提供的最大带宽
*/
if (b[j][k] > bmaxs[j]) {
bmaxs[j] = b[j][k];
}
/**
* if···语句的主要逻辑;
* 获得所有供应商中提供的最小带宽
*/
if (b[j][k] < bmin) {
bmin = b[j][k];
}
//获得某个供应商的某种设备对应的价格
p[j][k] = Integer.parseInt(s[k * 2 + 2]);
}
}
/**
* for循环的主要逻辑:
* 获得所有供应商中最大带宽中的最小值,保存在bmax中
*/
for (int j = 0; j < num; j++) {
if (bmaxs[j] < bmax) {
bmax = bmaxs[j];
}
}
System.out.println("bmin:" + bmin);
System.out.println("bmax:" + bmax);
bp = 0;
for (int j = bmin; j <= bmax; j++) {//在最小带宽和最大带宽之间做循环
sum = 0;
for (int k = 0; k < num; k++) {//在供应商之间循环
temp = Integer.MAX_VALUE;
for (int h = 0; h < n[k]; h++) {
/**
* if···语句的逻辑其实就是求最大B时的最小p
*/
if (b[k][h] >= j && p[k][h] < temp) {
//每一个供应商的设备的最小价格
temp = p[k][h];
}
}
//获得每个供应商中,当b最大时且p最小时的p的价格之和
System.out.println("temp:" +temp);
sum += temp;
}
/**
* if···语句的主要逻辑:
* 获得最大bp
*/
if ((double) j / sum > bp) {
System.out.println( "j:"+j);
System.out.println( "sum:"+sum);
bp = (double) j / sum;
System.out.println("bp:" + bp);
}
}
System.out.printf("%.3f\n", bp);
}
}
public static void main(String[] args) throws NumberFormatException,
IOException {
new POJ1018();
}
}
标签:poj1018,int,供应商,带宽,枚举,100,设备
From: https://blog.51cto.com/u_5290007/6251960