有个一个饭店,有n张桌子,每张桌子可以招待不同数量的客人,且不能拼桌,现在来了m批客人,每批客人有两个属性,一个是客人的总数,一个是他们消费(预计)的总额
请设计一个算法,计算出,店家能够获得的最大利润
我自己设计了一个算法,是这么思考的,先把客人排序,按照消费比(就是消费的金额除以人数)排序,然后吧桌子也排序
首先安排消费比最高的客人,从最小的桌子开始安排,如果桌子太小了,就换一个比它大一点的桌子
代码如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
class Table2 implements Comparable<Table2>{
int m;
boolean used;
int fee;
public Table2(int count){
m=count;
}
@Override
public int compareTo(Table2 o) {
if (o.m>=m) {
return -1;
}else {
return 1;
}
}
}
class Person implements Comparable<Person>{
int count;
int fee;
double rate;
boolean zhaodai;
public void computeRate(){
rate=fee*1.0/count;
}
public Person(int count,int fee){
this.count=count;
this.fee=fee;
}
@Override
public int compareTo(Person o) {
if (o.rate>=rate) {
return 1;
}else {
return -1;
}
}
public String toString(){
return count+" "+" "+fee+" "+rate;
}
}
public class Main {
public static void main(String[] args) {
int n,m;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
List<Table2> nList=new ArrayList<>();
for (int i = 0; i < n; i++) {
Table2 table=new Table2(sc.nextInt());
nList.add(table);
}
Collections.sort(nList);
List<Person> pList=new ArrayList<>();
for (int i = 0; i < m; i++) {
Person person=new Person(sc.nextInt(),sc.nextInt());
person.computeRate();
pList.add(person);
}
Collections.sort(pList);
// for (int i = 0; i < pList.size(); i++) {
// System.out.println(pList.get(i));
// }
for (int i = 0; i < pList.size(); i++) {
Person person=pList.get(i);
if (!person.zhaodai) {
for (int j = 0; j < nList.size(); j++) {
Table2 t=nList.get(j);
if (!t.used) {
if (person.count<t.m) {
t.used=true;
t.fee=person.fee;
person.zhaodai=true;
break;
}
}
}
}
}
int sum=0;
for (int j = 0; j < nList.size(); j++) {
Table2 t=nList.get(j);
sum+=t.fee;
}
System.out.println(sum);
}
}
/*
3 5
2 4 2
1 3
3 5
3 7
5 9
1 10
*/
上面的测试用例
3 5代表一共有3个桌子 5批客人
2 4 2 代表3张桌子的容量 分别是2 4 2
后面的1 3 代表第一批客人是1个人 消费3元 然后是第二批客人,共3人消费5元
这个测试用例 最后的答案是20
这个思路,是很直观的,但是,我没办法判断它的正确性
其实它是错的
输入:
1 2
4
2 4
3 5
输出:
4
可答案应该是5才对,因为题目要求的是总消费最高
感谢网友yg33717
后来,西安交大的张晨同学说,咱们用递归试试
思想就是 我随意让一个客人占用一张桌子(当然,至少客人得坐得下)计算一下收益率,然后把剩下的人和剩下的桌子递归调用安排的代码,就OK
我和我的小伙伴们都惊呆了,还能这么写
然后 过了20分钟,他给我发来这个下面的代码
import java.util.ArrayList;
import java.util.Scanner;
/**
* Created by zhangchen(chansonzhang@163.com) on 2016/9/7.
*/
public class TableGuest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int tableCount = 0;
int groupCount = 0;
while (scanner.hasNextLine()) {
tableCount = scanner.nextInt();
groupCount = scanner.nextInt();
ArrayList<Table> tables = new ArrayList<>();
ArrayList<GuestGroup> groups = new ArrayList<>();
for (int i = 0; i < tableCount; i++) {
tables.add(new Table(scanner.nextInt()));
}
for (int j = 0; j < groupCount; j++) {
groups.add(new GuestGroup(scanner.nextInt(), scanner.nextInt()));
}
int maxMoney = getMaxMoney(tables, groups);
System.out.println(maxMoney);
}
}
public static int getMaxMoney(ArrayList<Table> tables,
ArrayList<GuestGroup> groups) {
boolean[] isTableEmpty = new boolean[tables.size()];
boolean[] isGroupWait = new boolean[groups.size()];
for (int i = 0; i < isTableEmpty.length; i++) {
isTableEmpty[i] = true;
}
for (int j = 0; j < isGroupWait.length; j++) {
isGroupWait[j] = true;
}
int maxMoney = assignTable(tables, groups, isTableEmpty, isGroupWait,
tables.size(), groups.size());
return maxMoney;
}
public static int assignTable(ArrayList<Table> tables,
ArrayList<GuestGroup> groups, boolean[] isTableEmpty,
boolean[] isGroupWait, int tableLeft, int groupLeft) {
if (tableLeft == 0 || groupLeft == 0) {
return 0;
}
int[][] values = new int[tables.size()][groups.size()];// 第i行j列表示将table
// i分配给group j
// 产生的价值
for (int i = 0; i < tables.size(); i++) {
for (int j = 0; j < groups.size(); j++) {
Table table = tables.get(i);
GuestGroup group = groups.get(j);
if (isTableEmpty[i] && isGroupWait[j]
&& table.getCapacity() >= group.getGuestCount()) { // 可以尝试分配
boolean[] isTableEmptyCurrent = isTableEmpty.clone();
boolean[] isGroupWaitCurrent = isGroupWait.clone();
isTableEmptyCurrent[i] = false;
isGroupWaitCurrent[j] = false;
values[i][j] = group.getMoney()
+ assignTable(tables, groups, isTableEmptyCurrent,
isGroupWaitCurrent, tableLeft - 1,
groupLeft - 1);
}
}
}
int maxMoney = 0;
for (int i = 0; i < tables.size(); i++) {
for (int j = 0; j < groups.size(); j++) {
if (values[i][j] > maxMoney) {
maxMoney = values[i][j];
}
}
}
return maxMoney;
}
}
class Table {
public Table(int capacity) {
this.capacity = capacity;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
private int capacity;
}
class GuestGroup {
public GuestGroup(int guestCount, int money) {
this.money = money;
this.guestCount = guestCount;
}
private int guestCount;
private int money;
public int getGuestCount() {
return guestCount;
}
public void setGuestCount(int guestCount) {
this.guestCount = guestCount;
}
public int getMoney() {
return money;
}
}
过了一会,他又说,伙计,伙计,我又有了新的想法,
有一个二维矩阵,m[i][j]=k表示第i这个桌子招待第j批客人,获得的收益是k元
然后,这个矩阵的每一行,每一列都只能选一个元素
最后求最大值
他问我:你不觉得这代码,很熟悉么?
我看了看,恩,感觉有点像八皇后问题,对呀,他把所有符合规则的排练都求出来,找出最大的 不就OK了么
子曰:子曰:不愤不启,不悱不发.举一隅不与三隅反,则不复也
张晨自己能把这个问题联系到八皇后上,牛逼呀
贤哉,晨也!
代码如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Scanner;
/**
* Created by zhangchen(chansonzhang@163.com) on 2016/9/7.
*/
public class TableGuest2 {
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
int tableCount=0;
int groupCount=0;
while(scanner.hasNextLine()){
tableCount=scanner.nextInt();
groupCount=scanner.nextInt();
ArrayList<Table> tables=new ArrayList<>();
ArrayList<GuestGroup> groups=new ArrayList<>();
for(int i=0;i<tableCount;i++){
tables.add(new Table(scanner.nextInt()));
}
for(int j=0;j<groupCount;j++){
groups.add(new GuestGroup(scanner.nextInt(),scanner.nextInt()));
}
int[][] assignmentMatrix=new int[tables.size()][groups.size()];
//将问题转化为在一个矩阵中,每行选择一个元素,这些元素不能在同一列,使得和最大
for(int i=0;i<tables.size();i++){
for(int j=0;j<groups.size();j++){
Table table=tables.get(i);
GuestGroup group=groups.get(j);
if(table.getCapacity() >= group.getGuestCount()){
assignmentMatrix[i][j] = group.getMoney(); //可以获利
}else { //不能分配
assignmentMatrix[i][j]=0;
}
}
}
// int maxMoney= getMaxMoney(tables, groups);
int maxMoney= getMaxMoney(assignmentMatrix);
System.out.println(maxMoney);
}
}
public static int getMaxMoney(ArrayList<Table> tables, ArrayList<GuestGroup> groups){
boolean[] isTableEmpty=new boolean[tables.size()];
boolean[] isGroupWait=new boolean[groups.size()];
for(int i=0;i<isTableEmpty.length;i++){
isTableEmpty[i]=true;
}
for(int j=0;j<isGroupWait.length;j++){
isGroupWait[j]=true;
}
HashMap<Integer,Integer> bestAssignment=assignTable(tables,groups,isTableEmpty,isGroupWait,tables.size(),groups.size());
for(Entry<Integer,Integer> assign:bestAssignment.entrySet()){
System.out.println("将第"+assign.getKey()+"桌分配给"+assign.getValue()+"组客户");
}
return calculateMoney(groups,bestAssignment);
}
public static int getMaxMoney(int[][] assignmengMatrix){
int rowCount=assignmengMatrix.length;
int colCount=assignmengMatrix[0].length;
ArrayList<int[]> results=new ArrayList<>();
assignTable(0,new int[rowCount],rowCount,colCount,results);
int maxMoney=0;
int[] bestAssignment = new int[rowCount];
for(int[] columns:results){
int total=0;
for(int row=0;row<rowCount;row++){
total+=assignmengMatrix[row][columns[row]];
}
if(total>maxMoney){
maxMoney=total;
bestAssignment=columns;
}
}
for(int i=0;i<bestAssignment.length;i++){
System.out.println("将第"+i+"桌分配给"+bestAssignment[i]+"组客户");
}
return maxMoney;
}
public static void assignTable(int row,int[] columns,int rowCount,int colCount,ArrayList<int[]> results){
if(row == rowCount){//找到有效的分配方案
results.add(columns.clone());
}else{
for(int col=0;col<colCount;col++){
if(checkValid(columns,row,col)){ //分配是否有效
columns[row]=col; //分配桌子
assignTable(row+1,columns,rowCount,colCount,results);
}
}
}
}
public static boolean checkValid(int[] columns,int row1,int col1){
for(int row2=0;row2<row1;row2++){
int col2=columns[row2];
if(col2==col1){ //某一组客户被重复分配
return false;
}
}
return true;
}
public static HashMap<Integer,Integer> assignTable(ArrayList<Table> tables, ArrayList<GuestGroup> groups,boolean[] isTableEmpty,boolean[] isGroupWait,int tableLeft,int groupLeft){
HashMap<Integer,Integer> bestAssignment=new HashMap<>();
if(tableLeft==0 || groupLeft ==0 ){
return bestAssignment;
}
int maxMoney=0;
for(int i=0;i<tables.size();i++){
for(int j=0;j<groups.size();j++){
Table table=tables.get(i);
GuestGroup group=groups.get(j);
if(isTableEmpty[i] && isGroupWait[j] && table.getCapacity()>=group.getGuestCount()){ //可以尝试分配
boolean[] isTableEmptyCurrent=isTableEmpty.clone();
boolean[] isGroupWaitCurrent=isGroupWait.clone();
isTableEmptyCurrent[i]=false;
isGroupWaitCurrent[j]=false;
HashMap<Integer,Integer> assighment=assignTable(tables, groups, isTableEmptyCurrent, isGroupWaitCurrent,tableLeft-1,groupLeft-1);
assighment.put(i,j);
int money=calculateMoney(groups,assighment);
if(money>maxMoney){
bestAssignment=assighment;
maxMoney=money;
}
}
}
}
return bestAssignment;
}
public static int calculateMoney(ArrayList<GuestGroup> groups,HashMap<Integer,Integer> assignment){
int total=0;
for(int groupIndex:assignment.values()){
total+=groups.get(groupIndex).getMoney();
}
return total;
}
}
///
以下为2016-9-14日补充
感谢"麦尔"对我文章的研究:
非常简单的问题,怎么搞得这么复杂,说明你的算法功底还很弱,继续加油学习哦,去TopCoder刷刷题
这个题如果你想到按个人消费排序你就完了,它要的收益最大,不在乎让最多的人就餐。
算法思路:
1.将总消费让从大到小排序 -- consume[m]
2.将桌子按容纳人数从小到大排序 -- capacity[n]
3.遍历consume[m],取出元素后,再从capacity[n]中找到大于或等于该元素人数的桌子,并在capacity[n]标记该桌已用,若没有桌子能容纳该批客则直接放弃该批客人。
就这么简单,考虑的是盈利最大,不是要考虑让更多的人就餐
最后想说我是一个三本学生...
哎怎么说呢,感觉读书把自己读傻了,这个问题本来就很生活化,回家问我不懂计算机的老妈,她估计都能想到这个方法,
我们这些研究生生却想到那么复杂的解法,真是读书读傻了,这是智商问题呀
以上为2016-9-14日补充
///