1. 程式人生 > >[NOIP模擬][好題]分玩具

[NOIP模擬][好題]分玩具

題目描述:
豆豆和豆沙正在分一些玩具,每個玩具有一個好玩值,每個人可以拿走任意數量的玩具,獲得的愉快度為最小的好玩值。現在豆豆先拿,每個人輪流操作,直到沒有玩具可以拿。豆豆想知道他能比豆沙多出多少愉快度?
輸入格式:
第一行 N 表示玩具個數。
接下來一行 N 個整數表示第 i 個玩具的好玩值。
輸出格式:
輸出一個整數表示最多多出的愉快度。
樣例輸入:

3
1 3 1

樣例輸出:

2

資料範圍:
對於 30% 的資料,N10
對於 70% 的資料,N1000
對於 100 %的資料,N10000000數值範圍109
注意:計算方式是每輪都會計算,不是最後總的算。還有這兩個人都是絕頂聰明的,即他們每次都是按照最優解拿的。
題目分析:


考試時,第一眼看到題,內心是這樣的:什麼?博弈論?NOIP不是不考嗎?十分鐘後······半個小時後······一個小時後,怎麼做,沒思路啊!!!
先上題解:
這裡寫圖片描述
其實兩個人的目標是一致的,都是想是使自己的最小值儘量大。因為一個人對於當前序列,當然是取最大的一部分最優秀(雖然取幾個是不清楚的),所以我們可以先排序。當有一個人取走一部分後,剩餘序列對於當前選擇的人,抉擇問題其實是一樣的,只是序列變短了。所以整道題只是一個不斷遞迴的子問題。
我們假設一段序列(從小到大),為1~n,假設我們序列現在又是1~j,肯定又存在1~j的最優情況,那麼假設我們已經求出了1~j的最優秀情況,記錄為f[j](記錄的是最後取的那個人總的多出的愉悅度),那麼對於另一個人,j+1有選與不選的情況,選的話相當於你的愉悅度就是a[j+1],那你多出的就是a[j+1]-f[j],如果不選那你的最優值就是前面的最優值即f[j](相當於從右時,他選到前面最優的情況所位於的地方)。此過程實際上是(從大到小選,即n開始)輪到某一個人操作時,他可以選到j,也可以選到j+1等,比較j與j+1,如果他選j+1,那他的愉悅度就是a[j+1],而另外一個人在剩餘的1~j會選出對於他最優情況(相當於他變成先手多出的最大情況),那最後總的貢獻就是a[j+1]-f[j],如果選到j,與此同理。
附程式碼:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<iomanip>
#include<cmath>
#include<cctype>
#include<algorithm>
using namespace std;

const int N=1e6+5;
int n,a[N],f[N],maxans; 

int
readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } int main() { //freopen("toy.in","r",stdin); //freopen("toy.out","w",stdout); n=readint(); for(int i=1;i<=n;i++) a[i]=readint(); sort(a+1,a+n+1); maxans=a[1]; for(int i=1;i<=n;i++) { f[i]=maxans; maxans=max(a[i+1]-f[i],maxans);//其實你也可以合併寫成f[i]=max(f[i-1],a[i]-f[i-1]) } printf("%d",maxans); return 0; }