1. 程式人生 > 實用技巧 >CodeForces - Problem 1446 - Knapsack - 思維

CodeForces - Problem 1446 - Knapsack - 思維

CodeForces - Problem 1446 - Knapsack - 思維

對於所有物品按照重量從小到大排序,然後從後向前遍歷:

  • 對於重量已經超過了\(\omega\)的物品,將其忽略掉;
  • 對於重量處於\([\lceil \frac{\omega}{2} \rceil,\omega]\)的物品(如果存在),則直接選取這個物品,輸出答案
  • 對於所有重量小於\(\lceil \frac{\omega}{2} \rceil\)的物品,從大到小取物品,直到取完.如果中途發現累計重量處於\([\lceil \frac{\omega}{2} \rceil,\omega]\),輸出答案; 如果取完時重量仍小於\(\lceil \frac{\omega}{2} \rceil\)
    ,輸出不存在.

本題思維主要在於第三條: 當我們從所有重量處在\([\lceil \frac{\omega}{2} \rceil,\omega]\)的物品中挑選時:先去重量最大的物品,其重量處於\((0,\lceil \frac{\omega}{2} \rceil)\),接下來再取次大的,由於兩個物品重量都處於\((0,\lceil \frac{\omega}{2} \rceil)\),因此重量和不會超過\(\omega\); 此時判斷累計重量是否處於\([\lceil \frac{\omega}{2} \rceil,\omega]\),如果仍然不是,說明累計重量處於\((0,\lceil \frac{\omega}{2} \rceil)\)

,那麼再取第三個物品後累計重量仍然不會超過\(\omega\),這樣以此類推,我們優先選擇重量大的,所得到的累計重量就會盡可能地大而又不超過\(\omega\),這樣做顯然符合題意;

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
struct node{
    ll v;
    int p;
};
node a[N];
int ans[N]; int len;
bool comp(const node&a,const node&b){
    return a.v < b.v;
}
 
int find_begin(int n,ll w){
    int l = 1, r = n;
    while(l < r){
        int mid = l+r>>1;
        if(a[mid].v >= w){
            r = mid;
        }else{
            l = mid+1;
        }
    }
    return l;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        len=0;
        int n;
        ll w;
        scanf("%d%lld",&n,&w);
        for(int i = 1; i <= n; i++){
            scanf("%lld",&a[i].v);
            a[i].p = i;
        }
 
        sort(a+1,a+1+n,comp);
 
        int p = find_begin(n,(w+1)/2);
        int ptr = 0;
        if(a[p].v > w){
            ptr = p-1;
        }else if(a[p].v <= w && a[p].v >= (w+1)/2){
            printf("1\n%d\n",a[p].p);
            continue;
        }else{
            ptr = p;
        }
        int flag = 0;
        ll sum = 0;
        for(int i = ptr; i >= 1; i--){
            if(sum + a[i].v <= w){
                sum += a[i].v;
                ans[++len] = a[i].p;
            }
 
            if(sum >= (w+1)/2){
                flag = 1;
                break;
            }
        }
 
        if(flag){
            sort(ans+1,ans+1+len);
            int first = 1;
            printf("%d\n",len);
            for(int i = 1; i <= len; i++){
                if(first)first=0;else putchar(' ');
                printf("%d",ans[i]);
            }
            putchar('\n');
        }else{
            printf("-1\n");
        }
    }
 
    // system("pause");
    return 0;
}