4月 27, 2008

【解題】Ecological Bin Packing

@
ACM Volume I 102 - Ecological Bin Packing


Background

Bin packing, or the placement of objects of certain weights into different bins subject to certain constraints, is an historically interesting problem. Some bin packing problems are NP-complete but are amenable to dynamic programming solutions or to approximately optimal heuristic solutions.

In this problem you will be solving a bin packing problem that deals with recycling glass.


The Problem

Recycling glass requires that the glass be separated by color into one of three categories: brown glass, green glass, and clear glass. In this problem you will be given three recycling bins, each containing a specified number of brown, green and clear bottles. In order to be recycled, the bottles will need to be moved so that each bin contains bottles of only one color.

The problem is to minimize the number of bottles that are moved. You may assume that the only problem is to minimize the number of movements between boxes.

For the purposes of this problem, each bin has infinite capacity and the only constraint is moving the bottles so that each bin contains bottles of a single color. The total number of bottles will never exceed 231.


The Input

The input consists of a series of lines with each line containing 9 integers. The first three integers on a line represent the number of brown, green, and clear bottles (respectively) in bin number 1, the second three represent the number of brown, green and clear bottles (respectively) in bin number 2, and the last three integers represent the number of brown, green, and clear bottles (respectively) in bin number 3. For example, the line 10 15 20 30 12 8 15 8 31

indicates that there are 20 clear bottles in bin 1, 12 green bottles in bin 2, and 15 brown bottles in bin 3.

Integers on a line will be separated by one or more spaces. Your program should process all lines in the input file.


The Output

For each line of input there will be one line of output indicating what color bottles go in what bin to minimize the number of bottle movements. You should also print the minimum number of bottle movements.

The output should consist of a string of the three upper case characters 'G', 'B', 'C' (representing the colors green, brown, and clear) representing the color associated with each bin.

The first character of the string represents the color associated with the first bin, the second character of the string represents the color associated with the second bin, and the third character represents the color associated with the third bin.

The integer indicating the minimum number of bottle movements should follow the string.

If more than one order of brown, green, and clear bins yields the minimum number of movements then the alphabetically first string representing a minimal configuration should be printed.

Sample Input

1 2 3 4 5 6 7 8 9
5 10 5 20 10 5 10 20 10


Sample Output

BCG 30
CBG 50


解題思考

  看到這一題,我ㄧ開始的想法是:先找出這9個數字的最大值,再找出未被選擇的顏色與桶子中的最大值,接著同樣再找出未被選擇的顏色與桶子中的最大值。然後瓶子總和減掉這三個數字就是移動的最小值了。

  不過看到題目,假如出現同樣的最小值,則輸出字典順序最小的組合(間接的告訴你這是重點?)。再看到顏色順序是B-G-C,顯然我這方式是行不通的,因為最後必定會出現順序錯誤的問題。

  讓我們稍微轉個角度,既然顏色的順序是麻煩,就直接根據顏色順序來選。也就是說,直接照著字母順序,先選Clear要使用哪個桶子,再選Brown要使用哪個桶子,而最後那個桶子就是Green的。

  在這裡,我使用暴力法求出每種可能,再得到之中的最小值。由於已經是依照字母順序選擇,假如出現相同的最小值,則後得出的字典順序必小於先得到的。因此,如此便不用擔心組合順序的問題。


參考解答(C++)

#include <iostream>
#include <limits.h>

using namespace std;

bool select(int);

int bin[9], used[3], minn, n;
char pack[3];
const char color[] = {'B', 'C', 'G'};
const int order[] = {0, 2, 1};

int main(void)
{
    int get;

    // 確定能不能抓到第一筆資料
    while (cin >> get)
    {
        // 變數初始化
        n = get;
        minn = INT_MAX;
        for (int i = 0; i < 3; i++)
        {
            used[i] = false;
        }

        bin[0] = get;
        for (int i = 1; i < 9; i++)
        {
            cin >> bin[i];
            n += bin[i];
        }

        // 呼叫三層遞迴的第一層
        select(0);

        cout << pack[0] << pack[1] << pack[2] << " " << minn << endl;
    }

#ifndef ONLINE_JUDGE
    system("pause");
#endif
}

bool select(int m)
{
    bool ok = false;

    for (int i = 0; i < 3; i++)
    {
        if (used[i]) { continue; }

        n -= bin[i * 3 + order[m]];
        used[i] = true;

        // 假如還沒選到最後一個桶子
        if (m < 2)
        {
            if (select(m + 1))
            {
                pack[i] = color[m];
                ok = true;
            }
        }

        // 假如是目前最小值
        else if (n < minn)
        {
            minn = n;
            pack[i] = color[m];
            ok = true;
        }

        used[i] = false;
        n += bin[i * 3 + order[m]];
    }

    return ok;
}

4 回覆:

智涵 提到...

這是我的做法
很直覺
原本有想分化資料結構來分別桶子和瓶子
不過還是算了
//使用語言:VC#2008
static void Main(string[] args)
{
StreamReader sr = new StreamReader(@"C:\input.txt");
StreamWriter sw = new StreamWriter(@"C:\output.txt");
do
{
string[] strArr = sr.ReadLine().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int[] p = new int[9];
for (int i = 0; i < 9; i++)
p[i] = int.Parse(strArr[i]);
int c, min = int.MaxValue;
string result = string.Empty;
//BCG
c = p[1] + p[2] + p[3] + p[4] + p[6] + p[8];
if (c < min)
{
min = c;
result = "BCG ";
}
//BGC
c = p[1] + p[2] + p[3] + p[5] + p[6] + p[7];
if (c < min)
{
min = c;
result = "BGC ";
}
//CBG
c = p[0] + p[1] + p[4] + p[5] + p[6] + p[8];
if (c < min)
{
min = c;
result = "CBG ";
}
//CGB
c = p[0] + p[1] + p[3] + p[5] + p[7] + p[8];
if (c < min)
{
min = c;
result = "CGB ";
}
//GBC
c = p[0] + p[2] + p[4] + p[5] + p[6] + p[7];
if (c < min)
{
min = c;
result = "GBC ";
}
//GCB
c = p[0] + p[2] + p[3] + p[4] + p[7] + p[8];
if (c < min)
{
min = c;
result = "GCB ";
}
sw.Write(result + min);
if (sr.Peek() != -1)
sw.WriteLine();
else
break;
} while (true);
sr.Close();
sw.Flush();
sw.Close();
}

小參 提到...

唔...貼上來排版跑掉了@@"
建議可以貼在這裡:
http://src.wtgstudio.com/
支援的語言對我來說還滿堪用的XD

看了一下, 真的滿直覺的@@"
我是覺得若是運用迴圈
程式應該可以精簡許多XD

const int s[6][3] = {{0, 5, 7}, {0, 4, 8}, {2, 3, 7}, {2, 4, 6}, {1, 3, 8}, {1, 5, 6}};
const char select[6][] = {"BCG", "BGC", "CBG", "CGB", "GBC", "GCB"};
sum = p[0] + p[1] + ... + p[8];
for (int i = 0; i < 9; i++)
{
  int c = sum;
  for (int j = 0; j < 3; j++)
  {
    c -= s[i][j];
  }

  if (c < min)
  {
    min = c;
    result = select[i];
  }
}

大致寫一下
不懂C#, 寫得滿亂的請見諒@@"

小參 提到...

更正, 不小心手誤寫錯了
第一個for迴圈應該是 0 ~ 5
for (int i = 0; i < 6; i++)

kgame 智涵 提到...

中間應該是c -= p[s[i][j]];

張貼留言