*【CodeForces - 859C 】Pie Rules (博弈dp,时光倒流)
題干:
You may have heard of the pie rule before. It states that if two people wish to fairly share a slice of pie, one person should cut the slice in half, and the other person should choose who gets which slice. Alice and Bob have many slices of pie, and rather than cutting the slices in half, each individual slice will be eaten by just one person.
The way Alice and Bob decide who eats each slice is as follows. First, the order in which the pies are to be handed out is decided. There is a special token called the "decider" token, initially held by Bob. Until all the pie is handed out, whoever has the decider token will give the next slice of pie to one of the participants, and the decider token to the other participant. They continue until no slices of pie are left.
All of the slices are of excellent quality, so each participant obviously wants to maximize the total amount of pie they get to eat. Assuming both players make their decisions optimally, how much pie will each participant receive?
Input
Input will begin with an integer?N?(1?≤?N?≤?50), the number of slices of pie.
Following this is a line with?N?integers indicating the sizes of the slices (each between?1?and?100000, inclusive), in the order in which they must be handed out.
Output
Print two integers. First, the sum of the sizes of slices eaten by Alice, then the sum of the sizes of the slices eaten by Bob, assuming both players make their decisions optimally.
Examples
Input
3 141 592 653Output
653 733Input
5 10 21 10 21 10Output
31 41Note
In the first example, Bob takes the size?141?slice for himself and gives the decider token to Alice. Then Alice gives the size?592?slice to Bob and keeps the decider token for herself, so that she can then give the size?653?slice to herself.
題目大意:
有一個長度為N的權值數組,兩個人A,B。定義一種權力X為:決定當前這個數組元素分給誰。下次權利X授予沒有分得前一個元素的人。如,現在A有權力,將當前數組元素分給自己,那么下一次權力X將被B獲得。如果A將數組元素分給B,那么A繼續擁有這個權利。一開始權利在A手中,每個人都會采取對自己最有利的方式分配。問最后兩個人各自的權值總和。
解題報告:
考慮dp求解,要明確dp的狀態只與是否有令牌有關,而與令牌在誰手里無關。因為不論令牌在誰手里,那個人都會盡可能的獲得最大的物品價值。
因此我們用dp[i]表示第i個物品開始分配,當前有令牌的人能獲得的最大物品價值,然而因為我們只知道初始有令牌的是Bob,因此我們要逆推:?dp[i] = max(dp[i + 1], sum[i + 1] - dp[i + 1] + val[i]) //?max(不要當前物品, 要)
其實相當于是你如果要從前往后推的話,需要關注令牌是否是同一個人拿著的,所以麻煩很多,而逆推就簡單很多,因為令牌是往后傳的,我們當前是有令牌的,所以下一個令牌給誰,是我們決定的,就比較好寫。
最后dp[1]就是Bob獲得的價值了。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; ll dp[MAX],a[MAX],sum[MAX]; int main() {int n;cin>>n;for(int i = 1; i<=n; i++) scanf("%lld",a+i);for(int i = n; i>=1; i--) sum[i] = sum[i+1] + a[i];for(int i = n; i>=1; i--) {dp[i] = max(dp[i+1],a[i] + (sum[i+1] - dp[i+1]));//不選,選 }printf("%lld %lld\n",sum[1] - dp[1],dp[1]);return 0 ;}錯誤代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; ll a[MAX],sum[MAX]; ll dp[MAX][2][2];//dp[i][j][k]代表第i個數并且第i個選沒選0/1,令牌在 0/1(Bob)手里的最大值 ,Bob獲得的最大值 int main() {int n;cin>>n;for(int i = 1; i<=n; i++) scanf("%lld",a+i);for(int i = 1; i<=n; i++) sum[i] = sum[i-1] + a[i];dp[1][1][1] = a[1];dp[1][0][1] = 0;for(int i = 2; i<=n; i++) {dp[i][0][0] = a[i] + max(dp[i-1][1][1] , dp[i-1][0][0]);//sum[i-1] - min(dp[i-1][1][1] , dp[i-1][0][0]);dp[i][0][1] = max(dp[i-1][1][0] , dp[i-1][0][1]);dp[i][1][0] = dp[i][0][0];dp[i][1][1] = dp[i][0][1] + a[i];}ll maxx = 0;maxx = max(maxx,dp[n][0][0]);maxx = max(maxx,dp[n][0][1]);maxx = max(maxx,dp[n][1][0]);maxx = max(maxx,dp[n][1][1]);printf("%lld %lld\n",sum[n] - maxx,maxx);return 0 ;} //適用于:可以選擇要或者不要,但是不要的話不會把分數給對方 的方法。 // for(int i = 2; i<=n; i++) { // dp[i][0][0] = max(dp[i-1][1][1] , dp[i-1][0][0]);//sum[i-1] - min(dp[i-1][1][1] , dp[i-1][0][0]); // // dp[i][0][1] = max(dp[i-1][1][0] , dp[i-1][0][1]); // dp[i][1][0] = dp[i][0][0]; // dp[i][1][1] = dp[i][0][1] + a[i]; // }想到這個狀態感覺可以就開始寫了, 但是有個問題啊,,這是個博弈dp,也就是說你不能只顧著Bob,所以這樣dp是錯誤的,因為對方也是用的最優決策你要知道這一點,所以就不太對。所以對于這種博弈dp,我們需要關注的是令牌,因為誰拿著令牌,誰就希望得到最優解。所以我們應該直接設定狀態為dp[i]為拿著令牌的人第i輪選擇到最后可以得到的最優解。
為什么倒著呢?因為這是求最優解問題,就跟數塔一樣,先考慮最后一步決策,然后倒著推。如果是計數,那就一般正著寫。
(其實貌似也是可以寫的?就是比較麻煩,你考慮令牌不在Bob手里的時候求的是Bob拿到的最小值 就行了吧?)
改了改但還是放棄了、、、
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; ll a[MAX],sum[MAX]; ll dp[MAX][2][2];//dp[i][j][k]代表第i個數并且第i個選沒選0/1,令牌在 0/1(Bob)手里的最大值 ,Bob獲得的最大值 int main() {int n;cin>>n;for(int i = 1; i<=n; i++) scanf("%lld",a+i);for(int i = 1; i<=n; i++) sum[i] = sum[i-1] + a[i];dp[1][1][1] = a[1];dp[1][0][1] = 0;for(int i = 2; i<=n; i++) {dp[i][0][0] = a[i] + sum[i-1]/*sum[i]*/ - max(sum[i-1] - dp[i-1][1][1] , sum[i-1] - dp[i-1][0][0]);//sum[i-1] - min(dp[i-1][1][1] , dp[i-1][0][0]);dp[i][0][1] = max(dp[i-1][1][0] , dp[i-1][0][1]);dp[i][1][0] = dp[i][0][0] - a[i];dp[i][1][1] = dp[i][0][1] + a[i];}ll maxx = 0;maxx = max(maxx,dp[n][0][0]);maxx = max(maxx,dp[n][0][1]);maxx = max(maxx,dp[n][1][0]);maxx = max(maxx,dp[n][1][1]);printf("%lld %lld\n",sum[n] - maxx,maxx);return 0 ;} /* 3 10 20 40 30 40 */?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的*【CodeForces - 859C 】Pie Rules (博弈dp,时光倒流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sws.exe - sws是什么进程 有
- 下一篇: 【牛客 - 21302】被3整除的子序列