// 此博文为迁移而来,写于2015年6月7日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w3bs.html
1、前言
今天考试状态稍微好一点,错的地方和动态规划本身没有太大关系(然而没写高精度直接扣60分是不是太血腥= =)。其实我觉得今天题目好水。。。我都觉得水了。
2、题目
分析:根据双方的能量和当前回合数确定状态。设 f[i][j][k] 表示当前第 i 回合,A 有 j 点能量,B 有 k 点能量有的游戏局面数。若某方选择攻击,另一方必定选择攻击或防御。把攻击、防御、充能所有情况全部用A和B的能量变化量来体现,方程很容易写出,比较复杂,直接看代码。
代码:
-----------------------------------------------------------------------------------------------------
#include<cstdio>
#define MOD 1000000007
#define MAXN 205
#define PLUS(p) f[k][i][j]+=p
#define MOD 1000000007
#define MAXN 205
#define PLUS(p) f[k][i][j]+=p
int n,k,i,j;
long long f[MAXN][MAXN][MAXN],ans;
long long f[MAXN][MAXN][MAXN],ans;
int main()
{
freopen("bbj.in","r",stdin);
freopen("bbj.out","w",stdout);
scanf("%d",&n);
f[0][0][0]=1;
for (int k=1;k<=n;k++)
for (int i=0;i<=k;i++)
for (int j=0;j<=k;j++)
{
if (i<k && j<k) PLUS(f[k-1][i][j]); if (i<k-1 && j<k-1) PLUS(f[k-1][i+1][j+1]);
if (i<k-1 && j<k) PLUS(f[k-1][i+1][j]); if (i<k && j<k-1) PLUS(f[k-1][i][j+1]);
if (i && j) PLUS(f[k-1][i-1][j-1]); if (i && j<k) PLUS(f[k-1][i-1][j]);
if (i<k && j) PLUS(f[k-1][i][j-1]);
f[k][i][j]%=MOD;
}
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
{
ans+=f[n][i][j]; ans%=MOD;
}
printf("%d",ans);
return 0
}
{
freopen("bbj.in","r",stdin);
freopen("bbj.out","w",stdout);
scanf("%d",&n);
f[0][0][0]=1;
for (int k=1;k<=n;k++)
for (int i=0;i<=k;i++)
for (int j=0;j<=k;j++)
{
if (i<k && j<k) PLUS(f[k-1][i][j]); if (i<k-1 && j<k-1) PLUS(f[k-1][i+1][j+1]);
if (i<k-1 && j<k) PLUS(f[k-1][i+1][j]); if (i<k && j<k-1) PLUS(f[k-1][i][j+1]);
if (i && j) PLUS(f[k-1][i-1][j-1]); if (i && j<k) PLUS(f[k-1][i-1][j]);
if (i<k && j) PLUS(f[k-1][i][j-1]);
f[k][i][j]%=MOD;
}
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
{
ans+=f[n][i][j]; ans%=MOD;
}
printf("%d",ans);
return 0
}
----------------------------------------------------------------------------------------------------
注意:需要开long long。
分析:先讲yxj的正解。f[i][j]表示起点到(i,j)的最短路,按列转移。这固然可以,但是我大概看了一下所有AC的代码没有一个是跑的最短路,所以接下来我来讲讲各种奇怪的做法。方法二:由于这道题可以向上走,所以这并不能直接从上从左来转移。同样我们还是定义f[i][j]表示起点到(i,j)的距离,然而我们注意到最左边的一列必定由上至下到达(因为不能向左走),所以先求出来;然后从左至右按列转移,首先我们先假设不能向上走,即普通的棋盘型DP,然后我们将当前那一列从上至下,再从下至上进行数据更新(即能够向上走所得到的更优解),即:f[i][j]=min(f[i][j-1],f[i-1][j],f[i+1][j])+map[i][j]。我就是这么做的,下面的代码也就是这样的,开始我觉得这种想法有问题,但是我问了yxj他说应该可以。。。那就好。还有用线段树维护的,此处省略。
代码:
----------------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#define MAXN 1005
#define INF 0x7f7f7f7f
#include<cstring>
#define MAXN 1005
#define INF 0x7f7f7f7f
typedef long long ll;
ll min(ll a,ll b) { return (a<b)?a:b; }
ll n,m,map[MAXN][MAXN],f[MAXN][MAXN];
int main()
{
{
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
scanf("%I64d %I64d",&n,&m);
memset(f[0],INF,sizeof(f[0]));
memset(f[n+1],INF,sizeof(f[n+1]));
f[0][1]=0;
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++) scanf("%I64d",&map[i][j]);
for (ll i=1;i<=n;i++) f[i][1]=f[i-1][1]+map[i][1];
for (ll j=2;j<=m;j++)
{
for (ll i=1;i<=n;i++) f[i][j]=min(f[i][j-1],f[i-1][j])+map[i][j];
for (ll i=n;i>=1;i--) f[i][j]=min(f[i+1][j]+map[i][j],f[i][j]);
freopen("walk.out","w",stdout);
scanf("%I64d %I64d",&n,&m);
memset(f[0],INF,sizeof(f[0]));
memset(f[n+1],INF,sizeof(f[n+1]));
f[0][1]=0;
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++) scanf("%I64d",&map[i][j]);
for (ll i=1;i<=n;i++) f[i][1]=f[i-1][1]+map[i][1];
for (ll j=2;j<=m;j++)
{
for (ll i=1;i<=n;i++) f[i][j]=min(f[i][j-1],f[i-1][j])+map[i][j];
for (ll i=n;i>=1;i--) f[i][j]=min(f[i+1][j]+map[i][j],f[i][j]);
for (ll i=1;i<=n;i++) f[i][j]=min(f[i+1][j]+map[i][j],f[i][j]);
}
}
printf("%I64d",f[n][m]);
return 0;
}
return 0;
}
----------------------------------------------------------------------------------------------------
分析:最水的一道题啦。。。我就抱着这样的心态把他第一个给做完了,然而江哥这个#%#¥%@#。。。我开了 long long 然而这并不行。。。要写高精度存储!方程略,裸棋盘型DP。
代码:
----------------------------------------------------------------------------------------------------
#include<cstdio>
#define MAXN 1005
int max(int a,int b) { return (a>b)?a:b; }
int n,m,map[MAXN][MAXN];
long long f[MAXN][MAXN];
int main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) scanf("%d",&map[i][j]);
f[0][1]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (map[i][j]) continue;
if (!map[i-1][j]) f[i][j]+=f[i-1][j];
if (!map[i][j-1]) f[i][j]+=f[i][j-1];
}
printf("%I64d",f[n][m]);
return 0;
}
----------------------------------------------------------------------------------------------------
分析:这道题是这次考试最麻烦的,我只搞出了30分的暴力。看了题解,这道题还是需要用到许多和数学相关的东西,我们慢慢来分析一下,如下图(公式比较多,直接放在图片上算了)
代码:
----------------------------------------------------------------------------------------------------
#include<cstdio>
#define MAXN 1005
#define MOD 1000000007
#define DFS_next if (y==n) DFS(x+1,1); else DFS(x,y+1)
#define MAXN 1005
#define MOD 1000000007
#define DFS_next if (y==n) DFS(x+1,1); else DFS(x,y+1)
typedef long long ll;
ll ans,n,fac[MAXN]={1},c[MAXN][MAXN],f[MAXN]={1};
void DP()
{
for (ll i=0;i<=n;i++)
{
for (ll i=0;i<=n;i++)
for (ll j=0;j<=i;j++)
if (!j) c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
for (ll i=1;i<=n;i++) fac[i]=(fac[i-1]*i)%MOD;
for (ll i=1;i<=n;i++)
{
if (!j) c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
for (ll i=1;i<=n;i++) fac[i]=(fac[i-1]*i)%MOD;
for (ll i=1;i<=n;i++)
{
for (ll j=0;j<=i;j++) f[i]=(f[i]+c[i][j]*c[i][j]%MOD*fac[j])%MOD;
f[i]=(f[i]*f[i])%MOD;
for (ll j=1;j<=i;j++)
{
ll tc=(c[i][j]*c[i][j])%MOD;
tc=(tc*fac[j])%MOD;
f[i]=(f[i]*f[i])%MOD;
for (ll j=1;j<=i;j++)
{
ll tc=(c[i][j]*c[i][j])%MOD;
tc=(tc*fac[j])%MOD;
tc=(tc*f[i-j])%MOD;
f[i]-=tc;
f[i] =(f[i]+MOD)%MOD;
}
}
}
f[i]-=tc;
f[i] =(f[i]+MOD)%MOD;
}
}
}
int main()
{
{
freopen("chessboard.in","r",stdin);
freopen("chessboard.out","w",stdout);
scanf("%d",&n);
DP();
freopen("chessboard.out","w",stdout);
scanf("%d",&n);
DP();
printf("%d",f[n]);
return 0;
}
return 0;
}
----------------------------------------------------------------------------------------------------