USACO 4.1 Fence Rails (fence8)

/*
既然要装下尽可能多的物品,那么就应该先选入小的物品。所以,先把物品按照重量递增排序。那么:
1)如果前k物品不能装入背包,那么即使把其中一个物品P换成k+1~R中的一个物品Q,
   由于Q的重量大于P,因此也绝对不可能成功装入背包。
2)如果前k个物品可以装入背包,那么前k-1个物品也一定能装入背包。
3)如果前k个物品不能装入背包,那么前k+1个物品也一定不能装入背包。
这样,一个最值问题就转化成了判定性问题,只要枚举出可行的最大k值就可以了。
从2、3两个结论中还可以得知可行性与k的关系是单调的,因此,可以使用二分查找优化枚举(貌似不二分也可以通过所有数据)。
为方便起见,记所有背包的容量和为boardsum,前k个物品重量和为railsum[k]。

1)重量大于最大背包容量的物品可以直接删去,容量小于最小物品重量的背包也可以直接删去。(这不算剪枝,应该是预处理)
2)如果可以把前k个物品全部装入,那么浪费掉的容量waste一定是boardsum-railsum[k]。当一个背包的剩余容量小于物品重量的最小值,
   即不能装入任何物品时,把剩余的容量加到current_waste中,一旦current_waste  waste,就说明这种情况一定不能找到解,应该回溯(比较强大)。
3)如果存在两个物品p1,p2的重量相等,因此可以把他们看作是一样的,所以可以直接固定p2的顺序,把它放在p1后面(非常强大)。
4)如果railsum[k]boardsum,那么这k个物品一定不能放入背包。
5)改变搜索顺序也可以加快速度,由于放入较大的物品是比较难的,因此dfs的时候可以从大的物品开始。
  (这点很容易被忘记,但有些时候改变顺序的确很有效)

第1个剪枝是本人自己想到的。2,3,4都是从NOCOW和google上搜到的,真的很难想到,尤其是第三个。
refer to Jimmy's Thoughts
*/
/*
Executing...
   Test 1: TEST OK [0.000 secs, 3520 KB]
   Test 2: TEST OK [0.000 secs, 3520 KB]
   Test 3: TEST OK [0.011 secs, 3520 KB]
   Test 4: TEST OK [0.011 secs, 3520 KB]
   Test 5: TEST OK [0.011 secs, 3520 KB]
   Test 6: TEST OK [0.022 secs, 3520 KB]
   Test 7: TEST OK [0.000 secs, 3520 KB]
   Test 8: TEST OK [0.011 secs, 3520 KB]
   Test 9: TEST OK [0.011 secs, 3520 KB]
   Test 10: TEST OK [0.000 secs, 3520 KB]
   Test 11: TEST OK [0.000 secs, 3520 KB]
   Test 12: TEST OK [0.000 secs, 3520 KB]
All tests OK.
Your program ('fence8') produced all correct answers!  This is your
submission #3 for this problem.  Congratulations!
*/
/*
ID: haolink1
PROG: fence8
LANG: C++
*/

//#include iostream
#include fstream
#include algorithm    // std::sort
using namespace std;

const int MAX_RAIL = 1024;

int N,R;
int rail[MAX_RAIL];
int board[50];
int boardsum;
int railsum[MAX_RAIL];
int cut_rail_from[MAX_RAIL];
int max_waste,cur_waste; 
ifstream fin("fence8.in");
ofstream cout("fence8.out");
bool SmallFirst(int a, int b){return a  b;}
bool BigFirst(int a, int b){return a  b;}

void init(){
    fin  N;
    for(int i = 0; i  N; i++){
        fin  board[i];
        boardsum += board[i];
    }
    fin  R;
    for(int i = 0; i  R; i++){
        fin  rail[i];
    }
    sort(rail,rail+R,SmallFirst);
    sort(board,board+N,BigFirst);
    //ignore impossible boards and rails
    while(rail[R - 1]  board[0]  R  0)    R --;
    while(N  0  board[N - 1]  rail[0])    N --;
    if(R == 0 || N == 0){   //The case there is no solution;
        cout  0  endl;
        exit(0);//Note to exit, not return;
    }
    railsum[0] = rail[0];
    for(int i = 1;i  R;i ++){
        railsum[i] = railsum[i - 1] + rail[i];
    }
}

bool DFS_Check(int index){
    if(cur_waste  max_waste) return 0;
    if(index  0) return 1;
    int start = 0;
    if(rail[index] == rail[index+1]) //Note the case index == 1022; so we allocate 1024 for rail; 
        start = cut_rail_from[index+1];
    for(int i = start; i  N; i++){
        if(board[i] = rail[index]){
            board[i] -= rail[index];
            cut_rail_from[index] = i;
            if(board[i]  rail[0]) cur_waste += board[i];
            int remain = DFS_Check(index-1);
            //restore;
            if(board[i]  rail[0]) cur_waste -= board[i];
            cut_rail_from[index] = -1;
            board[i] += rail[index];
            // We only need one of the solution to put remain rails;
            // Once we find it, return true right away;
            if(remain) return 1; 
        }
    }
    return 0;
}

void BinarySearch(){
    for(int i = 0; i  R; i++) cut_rail_from[i] = -1;
    int low = 0,hight = R - 1;
    for(; hight = 0  boardsum  railsum[hight]; hight--);
    while(low  hight){
        //Note here we plus 1 because when low +1 == hight, we want to check mid == hight; 
        int mid = (low + hight + 1)/2; 
        max_waste = boardsum - railsum[mid];
        if(DFS_Check(mid)) low = mid;
        else hight = mid - 1;
    }
    cout  low + 1  endl;
}

int main(){
    init();
    BinarySearch();
    return 0;
}

最新回复(0)
/jishul8fILnqklEdRtLQjfDjZ7L_2BL_2FJnivpSgOVJPhif8Pcs_3D4858434
8 简首页