log P1080國王遊戲
洛谷普及訓練場貪心的最後一題
思想上確實是貪心但因為數據範圍的問題我80%的時間都是在寫高精(魔鬼)
本題的貪心策略的求解過程幾乎是純數學推導,但一旦把握了這個方向想推出來還是比較容易的,接下來只要耐心把排序和高精寫好就可以了
那麽來說一下推導過程:
首先是本題的無後效性:每個大臣得到的金幣數並不會影響後續大臣得到的金幣數,因為每位大臣得到的金幣數都取決於自己右手的金幣數。
確定這一點之後,不妨讓我們請出隊尾的兩名大臣葉光迪和程馳(程馳為最後),他們左右手上的數字分別是x1,y1和x2,y2。
來分類討論看看他們是如何走位排序來滿足貪心國王的企圖:
假設前面所有大臣左手數字總和為w
1.葉光迪在程馳前面時,他得到的金幣數為 w/y1 ,而程馳得到的金幣數為 w*x1/y2 。因此最多金幣數gold1即為兩式中較大的一項
2.程馳在葉光迪前面時,同理,最多金幣數gold2為 w/y2 和w*x2/y1 中較大的一項
由題意得,既然葉光迪在程馳前面,那麽說明gold1一定小於gold2,不然不會這麽排序
本題最難理解的地方來了:若gold1<gold2,我們就可以得到 w*x1/y2<w*x2/y1 ,進而x1y1<x2y2
為什麽呢? 進行一下數學推導:1.w*x2/y1>w/y1 w*x1/y2>w/y2
2.gold1和gold2不可能分別等於w/y1和w/y2(不符題意,自行證明)
3.當gold1=w/y1(w/y1>w*x1/y2) w/y2<w*x2/y1 所以w*x1/y2<w*x2/yi
這樣,我們很愉快地得出了貪心策略:將左右手乘積小的大臣排在前面
感謝葉光迪和程馳大臣
1 #include<cstdio> 2 #include<iostream> 3 int n,l=1,a[100010],b[100010],c[100010]; 4 int g[1000010]; 5 void cheng(int x),chu(),sort(int l,int r); 6 int main() 7 { 8 scanf("%d %d %d",&n,&b[0],&c[0]); 9 for(int i=0;i<n;i++) 10 { 11scanf("%d %d",&b[i],&c[i]); 12 a[i]=b[i]*c[i]; 13 } 14 g[1]=b[0]; 15 for(int i=1;i<n;i++) 16 cheng(i); chu(); 17 for(int i=l;i>=1;i--) printf("%d",g[i]); 18 return 0; 19 } 20 21 void chu(){ 22 for(int i=l;i>=1;i--) 23 { 24 g[i-1]+=((g[i]%c[n])*10); 25 g[i]/=c[n]; 26 } 27 while(g[l]==0) l--; 28 if(l==0) printf("1\n"); 29 } 30 31 void cheng(int x){ 32 for(int i=1;i<=l;i++) 33 g[i]*=b[x]; 34 for(int i=1;i<=l;i++) 35 { 36 g[i+1]+=(g[i]/10); 37 g[i]%=10; 38 } 39 l++; 40 while(g[l]>9) 41 { 42 g[l+1]+=(g[l]/10); 43 g[l]%=10; 44 l++; 45 } 46 if(g[l]==0) l--; 47 } 48 49 void sort(int l,int r) 50 { 51 int le=l,ri=r,mid=a[(l+r)/2]; 52 while(le<=ri) 53 { 54 while(a[le]<mid) 55 le++; 56 while(a[ri]>mid) 57 ri--; 58 if(le<=ri) 59 { 60 int t=a[le]; 61 a[le]=a[ri]; 62 a[ri]=t; 63 64 t=b[le]; 65 b[le]=b[ri]; 66 b[ri]=t; 67 68 t=c[le]; 69 c[le]=a[ri]; 70 c[ri]=t; 71 le++; ri--; 72 } 73 } 74 if(l<ri) 75 sort(l,ri); 76 if(le<r) 77 sort(le,r); 78 }
log P1080國王遊戲