1. 程式人生 > >田忌賽馬

田忌賽馬

toolbar mit iostream namespace ogr std cst ring 3年

技術分享圖片

  • 留言板

「JoyOI1048」田忌賽馬

題目描述

中國古代的歷史故事“田忌賽馬”是為大家所熟知的。話說齊王和田忌又要賽馬了,他們各派出N匹馬,每場比賽,輸的一方將要給贏的一方200兩黃金,如果是平局的話,雙方都不必拿出錢。現在每匹馬的速度值是固定而且已知的,而齊王出馬也不管田忌的出馬順序。請問田忌該如何安排自己的馬去對抗齊王的馬,才能贏取最多的錢?

輸入

第一行為一個正整數n (n < = 1000) ,表示雙方馬的數量。 第二行有N個整數表示田忌的馬的速度。 第三行的N個整數為齊王的馬的速度。

輸出

僅有一行,為田忌賽馬可能贏得的最多的錢,結果有可能為負。

樣例輸入

3 92 83 71 95 87 74

樣例輸出

200 9018題庫的田忌賽馬,如果田忌輸了,輸出0,且似乎n<=3000。JoyOI隨意 本題原來是用貪心的思路,用田忌速度從小到大的每一匹馬去戰勝或打平齊王的最強的馬 像這樣
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include<iostream> #include<algorithm> using namespace std;
int main() { int qw[10001]={0},tj[10001]={0}; int n; cin>>n; long long ans=-n*200; for(int i=1;i<=n;i++) cin>>tj[i]; for(int i=1;i<=n;i++) cin>>qw[i]; sort(tj,tj+n+1); sort(qw,qw+n+1); for(int i=1;i<=n;i++) { for(int j=n;j>=1;j--)
{ if(tj[i]>qw[j]){qw[j]=999999;ans+=400;break;} else if(tj[i]==qw[j]){qw[j]=999999;ans+=200;break;} } } cout<<max(0,ans); system("pause"); return 0; }

是有反例的,因為看不符合歷史上賽馬的典故,用這種方法下等馬變成和下等馬賽平了,顯然不妥。

但是用最弱的馬故意輸最強的馬當然也是不可取的。

網上有種貪心做法是這樣

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int Max=10000; int tian[Max],king[Max]; int i,j,n; bool cmp(int a,int b) {return a>b;} int main() { cin>>n; for(i=1;i<=n;i++) {cin>>tian[i];} for(i=1;i<=n;i++) {cin>>king[i];} sort(tian+1,tian+1+n,cmp); sort(king+1,king+1+n,cmp); int ans=0; int ii,jj; for(i=1,j=1,ii=n,jj=n;i<=ii;) { if(tian[i]>king[j]){ans+=200;i++,j++;} else if(tian[i]<king[j]){ans-=200;j++,ii--;} else { if(tian[ii]>king[jj]) { ans+=200; ii--;jj--; } else { if(tian[ii]<king[j]) ans-=200; ii--,j++; } } } cout<<ans; return 0; }

1、開始也是先排序,可以使用sort快排; 2、然後將田忌最大的馬與國王進行比較; 3、如果田忌最大的馬大於國王,那麽就勝場++; 4、如果田忌最大的馬小於國王,那麽就一定會輸,所以用田忌最小的馬輸給國王最大的馬; 5、如果田忌最大的馬等於國王,那麽就比較最小的馬; 5。1、如果田忌最小的馬大於國王,那麽勝場++; 5。2、如果田忌最小的馬小於國王,那麽就輸給國王; 5。3、如果田忌最小的馬等於國王,就用田忌最小的馬對國王最大的馬,如果國王最大的馬大,那麽財產要減200; 還有動規的

1.思路
不妨用貪心思想來分析一下問題。因為田忌掌握有比賽的“主動權”,他總是根據齊王所出的馬來分配自己的馬,所以這裏不妨認為齊王的出馬順序是按馬的速度從高到低出的。由這樣的假設,我們歸納出如下貪心策略:
如果田忌剩下的馬中最強的馬都贏不了齊王剩下的最強的馬,那麽應該用最差的一匹馬去輸給齊王最強的馬。
如果田忌剩下的馬中最強的馬可以贏齊王剩下的最強的馬,那就用這匹馬去贏齊王剩下的最強的馬。
如果田忌剩下的馬中最強的馬和齊王剩下的最強的馬打平的話,可以選擇打平或者用最差的馬輸掉比賽。
2.反例
光是打平的話,如果齊王馬的速度分別是1 2 3,田忌馬的速度也是1 2 3,每次選擇打平的話,田忌一分錢也得不到,而如果選擇先用速度為1的馬輸給速度為3的馬的話,可以贏得200兩黃金。
光是輸掉的話,如果齊王馬的速度分別是1 3,田忌馬的速度分別是2 3,田忌一勝一負,仍然一分錢也拿不到。而如果先用速度為3的馬去打平的話,可以贏得200兩黃金。

3.解決方案
通過上述的三種貪心策略,我們可以發現,如果齊王的馬是按速度排序之後,從高到低被派出的話,田忌一定是將他馬按速度排序之後,從兩頭取馬去和齊王的馬比賽。有了這個信息之後,動態規劃的模型也就出來了!

4.DP方程
設f[i,j]表示齊王按從強到弱的順序出馬和田忌進行了i場比賽之後,從“頭”取了j匹較強的馬,從“尾”取了i-j匹較弱的馬,所能夠得到的最大盈利。
狀態轉移方程如下:
F[I,j]=max{f[i-1,j]+c[n-(i-j)+1,i],f[i-1,j-1]+c[j,i]}
其中g[i,j]表示田忌的馬和齊王的馬分別按照由強到弱的順序排序之後,田忌的第i匹馬和齊王的第j匹馬賽跑所能取得的盈利,勝為1,輸為-1,平為0。
結果用最大的乘以200即可。

5.解釋

為什麽F[I,j]=max{f[i-1,j]+c[n-(i-j)+1,i],f[i-1,j-1]+c[j,i]}可以呢?

因為你無論怎麽樣都是從前或者從後面取馬,而F[I,j]=max{f[i-1,j]+c[n-(i-j)+1,i],f[i-1,j-1]+c[j,i]}這個方程把所有可能的貪心情況都表示出來了。

C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include<iostream> #include<cstring> #include<algorithm> using namespace std; int f[3001][3001],a[3001],b[3001],c[3001][3001]; int n,ans=0; bool gz(int a,int b) {return a>b;} int main() { cin>>n; for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) f[i][j]=-999999; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; sort(a+1,a+n+1,gz); sort(b+1,b+n+1,gz); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i]>b[j])c[i][j]=1; else if(a[i]==b[j])c[i][j]=0; else c[i][j]=-1; f[0][0]=0; for(int i=1;i<=n;i++) for(int j=0;j<=i;j++) if(j>=1)f[i][j]=max(f[i-1][j]+c[n-(i-j)+1][i],f[i-1][j-1]+c[j][i]); else f[i][j]=f[i-1][j]+c[n-(i-j)+1][i]; for(int i=0;i<=n;i++) ans=max(f[n][i],ans); cout<<ans*200; return 0; }

田忌賽馬