不會博弈論的 yww 在和博弈論大師 yxq 玩一個遊戲。dom
有 \(n\) 種卡牌,第 \(i\) 種卡牌有 \(b_i\) 張。ui
yww 會先把全部 \(B=\sum_{i=1}^nb_i\) 張卡分紅兩堆,每堆 \(\frac{B}{2}\) 張。保證 \(B\) 是偶數。spa
他們會輪流從第一堆中取卡牌,每次取一張,yww 先取,直到取完爲止。設計
而後他們會輪流從第二堆中取卡牌,每次取一張,yxq 先取,直到取完爲止。code
取完卡牌後,他們會計算本身的得分。假設某人在某一堆中取了 \(x\) 張第 \(i\) 種卡牌,那麼就能得到 \(\lfloor\frac{x}{a_i}\rfloor c_i\) 分。排序
每一個人的最終得分是這我的在兩堆中的得分之和。遊戲
yxq 想最小化 yww 的得分。get
做爲一名博弈論大師,yxq 每步都會執行最優策略。string
yww 不會博弈論,因此請你幫 yww 求出他最多能得到多少分。it
記 \(A=\sum_{i=1}^na_i,B=\sum_{i=1}^nb_i\);
\(1\leq a_i\leq A\leq 2000,1\leq b_i\leq B\leq 500000,2\mid B,1\leq n\leq 2000,1\leq c_i\leq 3000\);
考慮對於一堆牌,yww 先手,他能得到多少分。
對於一種牌 \(i\),若是 \(b_i\equiv -1 \pmod {2a_i}\),那麼先開始拿這種牌的人能夠拿到 \(\lfloor \frac{b_i}{2a_i}\rfloor+1\) 張,其餘狀況都只能拿到 \(\lfloor\frac{b_i}{2a_i}\rfloor\) 張。
記 \(b_i\equiv -1 \pmod {2a_i}\) 的牌爲特殊的牌,按照 \(c_i\) 從大到小排序,記爲 \(d_1,d_2,\ldots,d_k\),那麼最終先手的得分是 \(\sum\limits_i \lfloor\frac{b_i}{2a_i}\rfloor c_i+\sum\limits_{2\nmid i} c_{d_i}\),後手的得分是 \(\sum\limits_i \lfloor \frac{b_i}{2a_i}\rfloor c_i+\sum\limits_{2\mid i} c_{d_i}\)。
這樣就能夠設計DP狀態了:
\(f_{i,j,p1,p2}\) 爲前 \(i\) 種牌,第一堆分了 \(j\) 張,第一堆有 \(p1\) 種特殊的牌,第二堆有 \(p2\) 種特殊的牌,yww 的最大收益。
轉移時枚舉第 \(i\) 種牌分多少到第一堆。
複雜度爲 \(O(B^2)\)。
注意到收益只與每種牌分到第一堆的牌數 \(\bmod {2a_i}\) 有關,那麼DP的時候就能夠只枚舉 模 \(2a_i\) 的值就行了。
還有一個問題,第一堆牌能不能湊出 \(\frac{B}{2}\) 張?
對於一種牌,假設咱們把 \(k\) 張牌放到了第一堆,那麼有 \(\lfloor\frac{b_i-k}{2a_i}\rfloor\) 組 \(2a_i\) 張牌能夠隨意分配。這個東西等於 \(\lfloor\frac{b_i}{2a_i}\rfloor\) 或 \(\lfloor\frac{b_i}{2a_i}\rfloor -1\)。咱們僞裝它等於 \(\lfloor\frac{b_i}{2a_i}\rfloor -1\)。
只可能有 \(O(\sqrt{A})\) 種不一樣的 \(a_i\),隨便DP一下就行了。
記 \(g_{i,j}\) 爲用了前 \(i\) 種 \(a_i\) 組出 \(j\) 張卡牌,第 \(i\) 種 \(a_i\) 最少要多少份(每份 \(2a_i\) 張)(\(-1\) 表示組不出)。
時間複雜度爲 \(O(A^2+B\sqrt A)\)
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<ctime> #include<functional> #include<cmath> #include<vector> #include<assert.h> //using namespace std; using std::min; using std::max; using std::swap; using std::sort; using std::reverse; using std::random_shuffle; using std::lower_bound; using std::upper_bound; using std::unique; using std::vector; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef std::pair<int,int> pii; typedef std::pair<ll,ll> pll; void open(const char *s){ #ifndef ONLINE_JUDGE char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } void open2(const char *s){ #ifdef DEBUG char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;} void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');} int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;} int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;} const int N=2010; const int M=500010; int f[N][2][2][4*N]; int s[N]; int qs[N]; int c[M]; int g[M]; struct info { int a,q,v; }; info a[N]; int cmp(info a,info b) { return a.v>b.v; } int n; int main() { open("49F"); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].a,&a[i].q,&a[i].v); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { s[i]=s[i-1]+4*a[i].a; qs[i]=qs[i-1]+a[i].q; } memset(f,0x80,sizeof f); f[0][0][0][0]=0; for(int i=1;i<=n;i++) for(int l1=0;l1<=1;l1++) for(int l2=0;l2<=1;l2++) { int x=a[i].q/(2*a[i].a)-1; for(int k=0;k<=a[i].q;k++) { int v1=k/(2*a[i].a); int v2=(a[i].q-k)/(2*a[i].a); if(v2<x) break; int temp=(v1+v2)*a[i].v; int p1=l1,p2=l2; if(v1*2*a[i].a+2*a[i].a-1==k) { p1?0:temp+=a[i].v; p1^=1; } if(v2*2*a[i].a+2*a[i].a-1==a[i].q-k) { p2?temp+=a[i].v:0; p2^=1; } for(int j=0;j<=s[i-1];j++) f[i][p1][p2][j+k]=max(f[i][p1][p2][j+k],f[i-1][l1][l2][j]+temp); } } for(int i=1;i<=n;i++) if(a[i].q/(2*a[i].a)-1>0) c[a[i].a]+=a[i].q/(2*a[i].a)-1; memset(g,-1,sizeof g); g[0]=0; for(int i=1;i<=s[n];i++) if(c[i]) { for(int j=0;j<=qs[n];j++) if(~g[j]) g[j]=0; else if(j>=2*i&&(g[j-2*i]>=0&&g[j-2*i]<c[i])) g[j]=g[j-2*i]+1; else g[j]=-1; } int ans=0; for(int i=0;i<=s[n]&&i<=qs[n]/2;i++) if(~g[qs[n]/2-i]) for(int i1=0;i1<=1;i1++) for(int i2=0;i2<=1;i2++) ans=max(ans,f[n][i1][i2][i]); printf("%d\n",ans); return 0; }