[USACO]Fence Rails

Fence Rails
Burch, Kolstad, and SchrijversFarmer John is trying to erect a fence around part of his field. He has decided on the shape of the fence and has even already installed the posts, but he's having a problem with the rails. The local lumber store has dropped off boards of varying lengths; Farmer John must create as many of the rails he needs from the supplied boards.

Of course, Farmer John can cut the boards, so a 9 foot board can be cut into a 5 foot rail and a 4 foot rail (or three 3 foot rails, etc.). Farmer John has an `ideal saw', so ignore the `kerf' (distance lost during sawing); presume that perfect cuts can be made.

The lengths required for the rails might or might not include duplicates (e.g., a three foot rail and also another three foot rail might both be required). There is no need to manufacture more rails (or more of any kind of rail) than called for the list of required rails.

PROGRAM NAME: fence8

INPUT FORMAT

Line 1:N (1 <= N <= 50), the number of boards
Line 2..N+1:N lines, each containing a single integer that represents the length of one supplied board
Line N+2:R (1 <= R <= 1023), the number of rails
Line N+3..N+R+1:R lines, each containing a single integer (1 <= ri <= 128) that represents the length of a single required fence rail

SAMPLE INPUT (file fence8.in)

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

OUTPUT FORMAT

A single integer on a line that is the total number of fence rails that can be cut from the supplied boards. Of course, it might not be possible to cut all the possible rails from the given boards.

SAMPLE OUTPUT (file fence8.out)

7
 
----------------------分割线--------------------
粗略看起来,这题和之前往光盘上放歌那道题比较像。但明显这个比较复杂,没有顺序限制。
下面是USACO的提示:
This is a high dimensionality multiple knapsack problem, so we just have to test the cases.
Given that the search space has a high out-degree, we will use depth first search with iterative deepening in order to limit the depth of the tree.
However, straight DFSID will be too slow, so some tree-pruning is necessary.
 
本题维度太大,无法用DP:N最大到50,这就至少需要一个50维的数组,而且数组大小即a[i]的最大值还没有限制,所以这个数据结构无法构造。我们用DFSID,但纯粹的DFSID太慢,需要剪枝。
每次DFSID的深度设为K,我们用以下策略来进行优化:
 
    显然,可以用贪心法来选取这K个rail,即只需要判断最小的K个rail是否可以获得。我们在数据准备阶段就将 board 和 rail 数组从小到大排序。
    容易忽略了一个问题就是搜索方向,我习惯性地从0-k-1来搜,结果在第3/4个test就超时了。从k-1 -- 0 这个方向才是合理的。因为大的rail是比小的rail难切的,小的rail可以从任何board上切出,但是可以因为将大的board提前分
割了一部分给小的rail而导致大的rail无法获得,故先切大的可以剪掉不合理的分支。另外,考虑极端的情况,如果最大的rail超过了最大的board,则这次DFSID必然失败,如果先切小的rail则做了很多的必然导致失败的搜索。
    如果A和B两个board剩余部分相等,则在切rail[i]的时候,从A切和从B切是无差别的。
    如果C和D两个rail是相等的,则从Board A切C,Board B 切D 与 从Board A切D,Board B切C。这两个切法是无差别的。于是我们通过相等rail限制其Board序号 非减顺序,来避免重复。
    还有一个可以提前判断出失败的方法。每个Board当其剩余的部分小于尚未被切的最小rail时(在我们rail从大到小切的顺序下,它就是rail[0]),剩余的部分肯定会被浪费掉。于是每次dfsid我们统计当前的浪费情况,如果大于sum(board)-sum.k(rail)
则此次切割必然失败
    有一个优化我没有用到:如果选则的k个rail中,rail[i] = board[j],则rail[i]从board[j]取出即为最优。这个很简单就可以证明,如果某最优解不是这个情况,调换一下相关几个rail按这种方式切割等效与该最优解。(由于我之前没想过这个优化,
再加上它的话代码变动比较大,而且感觉它的作用并没有那么大,故代码中没有使用这个剪枝)。
 
---------------------------程序-----------
/*
ID: zhangyc1
LANG: C++
TASK: fence8
*/
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;

ofstream fileout("fence8.out");
int N, arrBoard[50], arrBuf[50], R, arrRail[1023];
int nSumBoard = 0;
int arrSumRail[1023] = {0};
int nLen = 0;
bool dfsid(int nDepth);

int compare(const void* argv1, const void* argv2)
{
    return *((int*)argv1) - *((int*)argv2);
}

void prepairData()
{
    ifstream filein("fence8.in");
    filein >> N;
    for (int i = 0; i < N; i++)
    {
        filein >> arrBoard[i];
        nSumBoard += arrBoard[i];
    }
    filein >> R;
    for (int i = 0; i < R; i++)
    {
        filein >> arrRail[i];
    }
    qsort(arrBoard, N, sizeof(int), compare);
    qsort(arrRail, R, sizeof(int), compare);

    // 计算排序后,rail的前k项之和
    arrSumRail[0] = arrRail[0];
    for (int i = 1;i < R; i++)
    {
        arrSumRail[i] = arrSumRail[i - 1] + arrRail[i];
    }
    filein.close();
}

// Since finding a board from which to cut a longer rail is more difficult than finding a board for a shorter rail,
// we will perform the search in such that the longest rail is cut first, then the second longest rail
// search strategy : longest rail to shortest rail
bool dfsid(int nLastIndex, int nDepth)
{
    if (nDepth <= 0)
        return true;
    int nUseless = 0;

    // If, when cutting a board, we get a length of board less than the shortest rail, it is useless, and we can discard it from consideration.
    // This reduces the total amount of board-feet left from which to cut the rest of the rails.
    // prunning : If the wasted length is too much, we cannot get nLen rails.
    for (int i = 0;i < N; i++)
    {
        if (arrBuf[i] < arrRail[0])
        {
            nUseless += arrBuf[i];
        }
        // 浪费太多,无数获得
        if (nUseless > nSumBoard - arrSumRail[nLen - 1])
        {
            return false;
        }
    }

    for (int i = 0; i < N; i++)
    {
        // prunnint: If there are two boards of the same length, we need to check cutting the rail from only the first
        if (i > 0 && arrBuf[i] == arrBuf[i - 1]) // 其实这个地方剪的不彻底,因为arrBuf此时很可能已经无序,应该是从0--i-1中如果有与arrBuf[i]相等的,则此次循环可跳过
            continue;

        if (arrBuf[i] >= arrRail[nDepth])
        {
            // if two rails are of the same length, then cutting the first from board A and the second from board B
            // is the same as cutting the first from board B and the second from board A.
            // so within sets of rails of the same length, we will ensure that the rails are cut from boards of non-decreasing index.
            // prunning : if two rails are of the same length, then the two rails are cut from non-decreasing index
            if (i && i < nLastIndex && arrRail[nDepth] == arrRail[nDepth + 1])
                continue;

            arrBuf[i] -= arrRail[nDepth];
            if (dfsid(i, nDepth - 1))
            {
                return true;
            }
            arrBuf[i] += arrRail[nDepth];
        }
    }
    return false;
}

void process()
{
    if (arrBoard[N -1] < arrRail[0])
    {
        fileout << 0 << endl;
        return;
    }

    int nMaxLen = R, nMinLen = 0;
    for (int i = 0; i < R; i++)
    {
        if (arrSumRail[i] > nSumBoard)
        {
            nMaxLen = i;
            break;
        }
    }
    // 折半进行dfsid
    while (nMinLen <= nMaxLen)
    {
        nLen = (nMaxLen + nMinLen) / 2;
        memcpy(arrBuf, arrBoard, N * sizeof(int));
        if (dfsid(-1, nLen - 1))
            nMinLen = nLen + 1;
        else
            nMaxLen = nLen - 1;
    }
    fileout << nMaxLen << endl;
}

int main(){
    prepairData();
    process();
    fileout.close();
    return 0;
}

转载于:https://www.cnblogs.com/doublemystery/archive/2013/03/01/2938450.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值