• 【CSA49F】【XSY3317】card 博弈论 DP


    题目大意

      不会博弈论的 yww 在和博弈论大师 yxq 玩一个游戏。

      有 (n) 种卡牌,第 (i) 种卡牌有 (b_i) 张。

      yww 会先把所有 (B=sum_{i=1}^nb_i) 张卡分成两堆,每堆 (frac{B}{2}) 张。保证 (B) 是偶数。

      他们会轮流从第一堆中取卡牌,每次取一张,yww 先取,直到取完为止。

      然后他们会轮流从第二堆中取卡牌,每次取一张,yxq 先取,直到取完为止。

      取完卡牌后,他们会计算自己的得分。假设某人在某一堆中取了 (x) 张第 (i) 种卡牌,那么就能获得 (lfloorfrac{x}{a_i} floor c_i) 分。

      每个人的最终得分是这个人在两堆中的得分之和。

      yxq 想最小化 yww 的得分。

      作为一名博弈论大师,yxq 每步都会执行最优策略。

      yww 不会博弈论,所以请你帮 yww 求出他最多能获得多少分。

      记 (A=sum_{i=1}^na_i,B=sum_{i=1}^nb_i)

      (1leq a_ileq Aleq 2000,1leq b_ileq Bleq 500000,2mid B,1leq nleq 2000,1leq c_ileq 3000)

    题解

      考虑对于一堆牌,yww 先手,他能获得多少分。

      对于一种牌 (i),如果 (b_iequiv -1 pmod {2a_i}),那么先开始拿这种牌的人可以拿到 (lfloor frac{b_i}{2a_i} floor+1) 张,其他情况都只能拿到 (lfloorfrac{b_i}{2a_i} floor) 张。

      记 (b_iequiv -1 pmod {2a_i}) 的牌为特殊的牌,按照 (c_i) 从大到小排序,记为 (d_1,d_2,ldots,d_k),那么最终先手的得分是 (sumlimits_i lfloorfrac{b_i}{2a_i} floor c_i+sumlimits_{2 mid i} c_{d_i}),后手的得分是 (sumlimits_i lfloor frac{b_i}{2a_i} floor c_i+sumlimits_{2mid i} c_{d_i})

      这样就可以设计DP状态了:

      (f_{i,j,p1,p2}) 为前 (i) 种牌,第一堆分了 (j) 张,第一堆有 (p1) 种特殊的牌,第二堆有 (p2) 种特殊的牌,yww 的最大收益。

      转移时枚举第 (i) 种牌分多少到第一堆。

      复杂度为 (O(B^2))

      注意到收益只与每种牌分到第一堆的牌数 (mod {2a_i}) 有关,那么DP的时候就可以只枚举 模 (2a_i) 的值就好了。

      还有一个问题,第一堆牌能不能凑出 (frac{B}{2}) 张?

      对于一种牌,假设我们把 (k) 张牌放到了第一堆,那么有 (lfloorfrac{b_i-k}{2a_i} floor)(2a_i) 张牌可以随意分配。这个东西等于 (lfloorfrac{b_i}{2a_i} floor)(lfloorfrac{b_i}{2a_i} floor -1)。我们假装它等于 (lfloorfrac{b_i}{2a_i} floor -1)

      只可能有 (O(sqrt{A})) 种不同的 (a_i),随便DP一下就好了。

      记 (g_{i,j}) 为用了前 (i)(a_i) 组出 (j) 张卡牌,第 (i)(a_i) 最少要多少份(每份 (2a_i) 张)((-1) 表示组不出)。

      时间复杂度为 (O(A^2+Bsqrt 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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    创建SSIS包—ETL中典型的数据清洗
    SSIS中的容器和数据流—数据转换(Transformations)
    创建SSIS包—循环和动态package
    SSIS中的容器和数据流—数据转换(Transformations)续
    创建SSIS包—建立端到端的package
    SQL点滴9—SQL Server中的事务处理以及SSIS中的内建事务
    SSIS中的容器和数据流—调试工具数据视图
    SQL点滴11—重置win7登录密码影响SQL登录
    SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比
    SSIS中的容器和数据流—举例说明数据转换任务
  • 原文地址:https://www.cnblogs.com/ywwyww/p/10214952.html
Copyright © 2020-2023  润新知