AcWing 165 小貓爬山

題目描述:

翰翰和達達飼養了N只小貓,這天,小貓們要去爬山。

經歷了千辛萬苦,小貓們終於爬上了山頂,但是疲倦的它們再也不想徒步走下山了(嗚咕>_<)。

翰翰和達達只好花錢讓它們坐索道下山。

索道上的纜車最大承重量爲W,而N只小貓的重量分別是C1、C2……CN。

當然,每輛纜車上的小貓的重量之和不能超過W。

每租用一輛纜車,翰翰和達達就要付1美元,所以他們想知道,最少需要付多少美元才能把這N只小貓都運送下山?

輸入格式

第1行:包含兩個用空格隔開的整數,N和W。

第2..N+1行:每行一個整數,其中第i+1行的整數表示第i只小貓的重量CiCi。

輸出格式

輸出一個整數,表示最少需要多少美元,也就是最少需要多少輛纜車。

數據範圍

1≤N≤18,
1≤Ci≤W≤10^8

輸入樣例:

5 1996
1
2
1994
12
29

輸出樣例:

2

分析:

本題與上一題都是求滿足一定條件下的最小分組數,整體思路一致,只是多了一些剪枝。

常見剪枝方法:

1.優化搜索順序,不同的搜索順序產生的搜索樹形態不同,如果先搜索較少分支的部分,後面再剪枝,就可以大幅減少搜索時間。

2.排除等效冗餘,如果搜索樹上幾條分支效果是一樣的,就可以只搜其中的一個分支。

3.可行性剪枝,比如題目限定總重量不超過w,一旦搜索過程中某條路徑出現總重量超過w的分支,就可以停止遍歷該分支。

4.最優性剪枝,要求的是最小值等屬性,當某個搜索代價超過了之前的最優解,就可以停止搜索,執行回溯。

5.記憶化搜索,搜索過程中可能會重複訪問一個節點,可以將該狀態的值存儲下來,下次訪問直接返回。

本題的搜索過程爲:搜索第u只小貓時,先嚐試把它放入現有的纜車中(如果放得下),最後嘗試新開一個纜車放進貓,搜索完所有的放貓組合,找出最優解。很明顯的剪枝是最優性剪枝,如果前面有個方案只花費了k輛纜車,後面搜索過程中枚舉到第k輛車時就不用繼續搜索了,因爲不可能更新最優解。可行性剪枝就是加入當前貓時重量超過w的情況就不再枚舉,比較不容易想到的是優化搜索順序,由於最優性剪枝的存在,我們希望儘可能的先枚舉纜車數少的情況,如果有一種方案只用了2輛車,就可以剪掉後面所有大於等於2的方案數了,如果我們知道怎樣枚舉花的纜車是最少的,就不用再去搜索了,所以我們只要用一種優於直接搜索的搜索順序即可。比如有四隻貓,重量分別爲2 4 6 8,如果我們按照這樣的順序枚舉,最先搜索的分支是第一輛車放2 4,第二三輛車放6和8,最先搜到的解是3輛;如果我們按照從大到小的順序去搜索,也就是8 6 4 2的順序,首先前兩輛車分別放8和6,第三隻貓放置第二輛車上,第四隻貓放在第一輛車上,這樣就只花了兩輛車了,所以按照從大到小的重量去枚舉貓,得到的解比較小,有利於最優性剪枝,這個優化搜索順序的方法差不多可以減少三倍的dfs時間。

當然,本題數據範圍小一方面是可以用暴搜做,另一方面也可以用狀態壓縮DP去做,f[i][j]表示放置小貓狀態爲j時第i輛車已經消耗重量的最小值,狀態轉移方程爲f[i][j  | 1<< k] = min(f[i][j] + w[k]),f[i][j] + w[k] <= w。f[i+1][j | 1 << k] = min(w[k]),f[i][j] + w[k] > w時,放不下就再開一輛。鑑於狀壓的思路比較複雜且未必優於剪枝過後的dfs,這裏就只用dfs去實現本題了。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20;
int c[N],car[N],len,n,w,ans = N;
void dfs(int u){
    if(len >= ans)  return;//最優性剪枝
    if(u == n){
        ans = len;
        return;
    }
    for(int i = 0;i < len;i++){
        if(car[i] + c[u] <= w){//可行性剪枝
            car[i] += c[u];
            dfs(u + 1);
            car[i] -= c[u];
        }
    }
    car[len++] = c[u];
    dfs(u + 1);
    car[--len] -= c[u];
}
int main(){
    cin>>n>>w;
    for(int i = 0;i < n;i++)    cin>>c[i];
    sort(c,c + n,[=](int x,int y){return x > y;});//優化搜索順序
    dfs(0);
    cout<<ans<<endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章