1. 程式人生 > >U33405 紐約 (二分)

U33405 紐約 (二分)

main 之前 ems pre true 開車 new .org int

【題目描述】

    牧民 Azone 需要多次往返於兩個草場之間運輸家當。為了順利轉場,Azone 決定花費 w元津巴布韋幣,購買一輛載重為 w 的汽車。共有 n 件家具需要搬運,每件家具的重量為 wi? 。Azone 每次出發前,會搬若幹件總重不超過 w 的物品上車:出發前,車是空載的,Azone 會選擇能搬上車的家具中最重的一件放上車(即該家具之前還未運走且放置該家具後汽車不會超載),然後在剩下的家具中繼續選擇一件能被搬走的最重的上車,持續裝車,直至剩下的家具都塞不上車。裝載完畢後,Azone 會開車運走這些家具,卸在目的地,再駕空車返回繼續運送,直至轉場完畢。Azone 希望在運送次數不超過 R 的情況下完成轉場,求 Azone 最少需要購置價值多少的車。

【題目鏈接】

    https://www.luogu.org/problemnew/show/U33405

【算法】

    直接二分結果不一定是最優解,存在w時可行而w+1不可行的情況。但是若w可行則w+Biase(偏置值>=max(w【i】))必定可行,所以先二分然後往前枚舉max(w【i】)個。重點是為什麽二分結果不一定是最優解,因為題目當中采取的裝載策略(貪心策略:取盡可能重)並非最優策略(貪心成立的時候記得是坐船問題:一個船最多坐兩個人,並且有載重限制,可以證明)。

【代碼——模擬貪心策略,對每一個家具遍歷已經開出的裝載集合,若能裝則裝否則重新開一個集合】

 1 #include <bits/stdc++.h>
 2
using namespace std; 3 int n,r,i,l,j,R,ans; 4 int a[2010],rec[2010]; 5 bool cmp(int a,int b) { return a>b; } 6 bool valid(int cur) 7 { 8 if(cur<a[1]) return false; 9 memset(rec,0,sizeof(rec)); 10 int tot=0; 11 for(i=1;i<=n;i++) { 12 int flag=0; 13 for(j=1;j<=tot;j++)
14 if(rec[j]+a[i]<=cur) { 15 rec[j]+=a[i],flag=1; 16 break; 17 } 18 if(!flag) rec[++tot]=a[i]; 19 if(tot>R) return false; 20 } 21 return true; 22 } 23 int main() 24 { 25 scanf("%d%d",&n,&R); 26 for(i=1;i<=n;i++) scanf("%d",&a[i]),r+=a[i]; 27 sort(a+1,a+n+1,cmp); 28 l=a[1]; 29 while(l<r) { 30 int mid=(l+r)>>1; 31 if(valid(mid)) r=mid; 32 else l=mid+1; 33 } 34 ans=l; 35 for(int i=1;i<=a[1]&&l-i>=a[1];i++) 36 if(valid(l-i)) ans=l-i; 37 printf("%d\n",ans); 38 return 0; 39 }

U33405 紐約 (二分)