dp day4-博弈论

先贴一个链接http://blog.csdn.net/qq1169091731/article/details/51942752

度娘和csdn都是好东西

洛谷p1857取石子

博弈论的题目,对于一种情况,如果有前驱情况为必败态,则该情况必为必胜态,若前驱情况都为必胜态,则该情况为必败态。


且若当前情况为必胜态,则应从步数最少的前驱必败态转换而来(尽快胜利),


若当前情况为必败态,则应从步数最多的必胜态转换而来(阻止对方胜利)。


s[i][0]表示i个石子时的步数,s[i][1]=-1表示当前为必败态,s[i][1]=1表示为必胜态。


p数组存质数,pr数组为判定用。


#include<bits/stdc++.h>
using namespace std;
int n,s[20010][2]={0},a[20]={0},pr[20010]={1,1},p[3000]={0},ma=0,sum=0;
inline int read(int &num)
{
    num=0;
    char c=getchar();
    for(;isdigit(c)==0;c=getchar());
    for(;isdigit(c)!=0;c=getchar())num=num*10+c-'0';
}


inline void init()
{
    read(n);
    for(int i=1;i<=n;++i)
    {
        read(a[i]);
        if(a[i]>ma)ma=a[i];//寻找最大值,以便线下操作
    }
    return;
}


inline void first()//筛法,pr[i]=0表示质数,pr[i]=1表示合数 
{
    for(int i=2;i<=ma;++i)
    if(pr[i]==0)
    {
        p[++sum]=i;//存储质数,可以节省时间
        for(int j=i+i;j<=ma;j+=i)pr[j]=1;
    }
    return;
}


inline void dp()
{
    s[0][1]=-1;s[0][0]=0;
    s[1][1]=-1;s[1][0]=0;
    s[2][1]=1;s[2][0]=1;
    s[3][1]=1;s[3][0]=1;//初始化几个点
    for(int i=4;i<=ma;++i)
    for(int j=sum;j>=1;--j)//j不能反过来,会WA
    if(p[j]<=i)
    { 
        if(s[i-p[j]][1]==-1)//前驱为必败态
        if(s[i][1]==0||s[i][1]==-1)//如果未判定状态
        {
            s[i][1]=1;//置必胜态
            s[i][0]=s[i-p[j]][0]+1;
        }
        else
        s[i][0]=min(s[i][0],s[i-p[j]][0]+1);//选取最小值

        if(s[i-p[j]][1]==1)//前驱为必败态
        if(s[i][1]==0)//如果未置状态
        {
            s[i][1]=-1;
            s[i][0]=s[i-p[j]][0]+1;
        }
        else if(s[i][1]==-1)//如果已置必败态
        s[i][0]=max(s[i][0],s[i-p[j]][0]+1);//寻找最大值
    }
    return;
}


inline void print()
{
    for(int i=1;i<=n;++i)
    if(s[a[i]][1]==-1)
    printf("-1\n");
    else printf("%d\n",s[a[i]][0]);
    return;
}


int main()
{
    init();
    first();
    dp();


洛谷p2734游戏A game

区间dp,设s[i][j]表示从第i个数到第j个数取能所获得的最大值,则这次选取一定让下一次的选取的最大值最小,

即形成s[i+1][j]和s[i][j-1]中更小的。

#include<bits/stdc++.h>
using namespace std;
int n,s[110][110]={0},a[110]={0},sum[110]={0};
inline int read(int &num)//熟悉的读入优化
{
	num=0;
	char c=getchar();
	for(;isdigit(c)==0;c=getchar());
	for(;isdigit(c)!=0;c=getchar())num=num*10+c-'0';
}

inline void init()
{
	read(n);
	for(int i=1;i<=n;i++)
	{
	    read(a[i]);
	    sum[i]=sum[i-1]+a[i];
	}
	return;
}

inline void dp()
{
	for(int i=1;i<=n;i++)s[i][i]=a[i];
	for(int len=2;len<=n;len++)
	for(int i=1;i<=n-len+1;i++)
	{
	    int j=i+len-1; 
	    s[i][j]=sum[j]-sum[i-1]-min(s[i+1][j],s[i][j-1]);
	}
	return;
}

int main()
{
        init();
	dp();
	printf("%d %d",s[1][n],min(s[1][n-1],s[2][n]));//第一人会让第二人获得的最小,所以用min
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值