您的位置:首页

第七届省赛赛前交流赛部分题解

2017-06-15 10:13 183 查看
A题: Yougth's Game[Ⅲ]( 区间dp )

这是在省赛前热身赛出的题目,可能是题目中实用到博弈的思想,非常多人都在做,并且在尝试暴力。

可是没有人往dp的方向上想。

题目类型:动态规划+博弈分析:题意描写叙述的非常清楚,就是选择在两端取数,当前取的数不仅可以影响下一次的结果,并且可以影响后面的结果。



。又是一个求最优值,那么是不是可以往dp的方向上想了。区间dp定义状态dp[ i ] [ j ] 为从 i 到 j 上A的得分,那么B的得分就是sum(i,j)-dp[ i ] [ j ]转移方程 : dp[i][j]=max(dp[i][j],a[i]+(sum[j]-sum[i]-dp[i+1][j]));
表示区间i--j取第一个
[b]dp[i][j]=max(dp[i][j],a[j]+(b[j-1]-b[i-1]-dp[i][j-1]));
表示区间i--j取最后一个
[/b]

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1200;
int a
,dp

,n;
int b
;
int main()
{
while(~scanf("%d",&n)&&n)
{
b[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];
}
for(int l=1;l<=n;l++)
{
for(int i=1,j=l;j<=n;i++,j++)
{
dp[i][j]=-99999999;
dp[i][j]=max(dp[i][j],a[i]+(b[j]-b[i]-dp[i+1][j]));
dp[i][j]=max(dp[i][j],a[j]+(b[j-1]-b[i-1]-dp[i][j-1]));
}
}
printf("%d\n",dp[1]
-(b
-dp[1]
));
}
return 0;
}


E:Yougth's Game[II]

题目类型:组合博弈
事实上就是一个sg函数,sg函数解说:解说

分析:题目能够抽象成一个取石子游戏,三堆石子x,y,x*y。那么就抽象成了经典的取石子游戏。
每次能够取得数目是给定的,谁最后取完谁赢。
求出三个数的sg值,然后异或就是ans

代码:
#include<stdio.h>
#include<string.h>
#include <string>
#include <iostream>
using namespace std;

const int N = 10008;
int s[108],t;
int sg
;
bool hash
;
void sg_solve(int *s,int t,int N)   //N求解范围 S[]数组是能够每次取的值。t是s的长度。
{
int i,j;
memset(sg,0,sizeof(sg));
for(i=1;i<=N;i++)
{
memset(hash,0,sizeof(hash));
for(j=0;j<t;j++)
if(i - s[j] >= 0)
hash[sg[i-s[j]]] = 1;
for(j=0;j<=N;j++)
if(!hash[j])
break;
sg[i] = j;
}
}

int main()
{
int x,y;
while(~scanf("%d%d",&x,&y))
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&s[i]);
sg_solve(s,n,N);
if((sg[x]^sg[y]^sg[x*y])==0)
printf("Yougth is best\n");
else
printf("No\n");
}
return 0;
}


H题:合并游戏

题目类型:状态压缩动态规划

分析:给出每两个石子合并蹦出的金币,求全部石子合并之后蹦出的金币,分析能够发现不同的合并顺序会有不一样的结果。然后满足最优子结构的性质。那就是确定某几个石子合并的到的最大金币永远是确定的,那么我们能够用dp来求解。而这个题目恰好是一个从0到全部合并的一个过程。用二进制压缩来表示状态。

定义状态:dp【st】表示状态st中为1的合并起来的最大值

状态转移方程:dp[state-(1<<j)]=max(dp[state-(1<<j)], dp[state]+a[i][j]);(每次都从 i 为1的点转移过来)

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 12;
int a

,dp[1<<N];
int main()
{
int n;
while(~scanf("%d",&n)&&n)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&a[i][j]);
memset(dp,0,sizeof(dp));
for(int state=(1<<n); state>=0; state--)
{
for(int i=0; i<n; i++)  //枚举初始合并点
{
if(state&(1<<i))
continue;
for(int j=0; j<n; j++)   //跟哪一个合并
{
if(!(state&(1<<j)))
continue;
if(i==j)
continue;
int newstate=state-(1<<j);
dp[newstate]=max(dp[newstate], dp[state]+a[i][j]);
}
}
}
int maxnum=0;
for(int i=0; i<(1<<n); i++)
maxnum=max(maxnum, dp[i]);
printf("%d\n", maxnum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: