题意:给出卒的坐标和敌方马的坐标,求卒不经过马的攻击范围从(0,0)点到达(n,m)的路径数
思路:设dp[ i ] [ j ] 为从(0,0)到达 ( i , j ) 点的路径数。可以得到转移方程:
d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d [ i ] [ j − 1 ] dp[i][j]=dp[i-1][j]+d[i][j-1] dp[i][j]=dp[i−1][j]+d[i][j−1]
递推时,跳过马的攻击点即可
#include <iostream>
#define ll long long
using namespace std;
int n,m,x,y;
ll dp[21][21];
int visit[21][21];
int moves[][2]={0,0,-2,1,-1,2,1,2,2,1,2,-1,1,-2,-1,-2,-2,-1};
int main()
{
cin>>n>>m>>x>>y;
dp[0][0]=1;
for(int i=0;i<=8;++i)
{
int nx=x+moves[i][0];
int ny=y+moves[i][1];
if(nx>=0&&nx<=n&&ny>=0&&ny<=m)
visit[nx][ny]=1;
}
for(int i=0;i<=n;++i)
{
for(int j=0;j<=m;++j)
{
if(!visit[i][j])
{
if(i)
dp[i][j]+=dp[i-1][j];
if(j)
dp[i][j]+=dp[i][j-1];
}
}
}
cout<<dp[n][m]<<endl;
return 0;
}
2、把第一维压缩成 2
注意:正确的代码是:dp[i&1][j]=dp[(i-1)&1][j]; 而不是:dp[i&1][j]+=dp[(i-1)&1][j];
原因:该位置的值代表的是(i-2,j)位置的路径数,再加上dp[ i-1 ][ j ] 的值,没有任何意义。
#include <iostream>
#define ll long long
using namespace std;
int n,m,x,y;
ll dp[2][21];
int visit[21][21];
int moves[][2]={0,0,-2,1,-1,2,1,2,2,1,2,-1,1,-2,-1,-2,-2,-1};
int main()
{
cin>>n>>m>>x>>y;
dp[0][0]=1;
for(int i=0;i<=8;++i)
{
int nx=x+moves[i][0];
int ny=y+moves[i][1];
if(nx>=0&&nx<=n&&ny>=0&&ny<=m)
visit[nx][ny]=1;
}
for(int i=0;i<=n;++i)
{
for(int j=0;j<=m;++j)
{
if(!visit[i][j])
{
if(i)
dp[i&1][j]=dp[(i-1)&1][j];
if(j)
dp[i&1][j]+=dp[i&1][j-1];
}
else
dp[i&1][j]=0;
}
}
cout<<dp[n&1][m]<<endl;
return 0;
}
3、直接压缩成一维
#include <iostream>
#define ll long long
using namespace std;
int n,m,x,y;
ll dp[21];
int visit[21][21];
int moves[][2]={0,0,-2,1,-1,2,1,2,2,1,2,-1,1,-2,-1,-2,-2,-1};
int main()
{
cin>>n>>m>>x>>y;
dp[0]=1;
for(int i=0;i<=8;++i)
{
int nx=x+moves[i][0];
int ny=y+moves[i][1];
if(nx>=0&&nx<=n&&ny>=0&&ny<=m)
visit[nx][ny]=1;
}
for(int i=0;i<=n;++i)
{
for(int j=0;j<=m;++j)
{
if(!visit[i][j])
{
if(j)
dp[j]+=dp[j-1];
}
else
dp[j]=0;
}
}
cout<<dp[m]<<endl;
return 0;
}
题意:从(1,1)到(n,m)传两张纸条,要求只能往下或者往右,两条路径不重复,并且使得同学的好心程度最大。求同学的好心程度
思路:从(1,1)到(n,m)最多要走n+m-2步,我们可以枚举步数。让两张纸条同时传送。设dp[step][i][j]表示走了step - 2步后,靠左的纸条在第i列,靠右的纸条在第j列。同时它们的横坐标是可以直接算出来的
转移方程: d p [ s ] [ i ] [ j ] = m a x ( d p [ s − 1 ] [ i − 1 ] [ j − 1 ] , d p [ s − 1 ] [ i − 1 ] [ j ] , d p [ s − 1 ] [ i ] [ j − 1 ] , d p [ s − 1 ] [ i ] [ j ] ) + a [ s − i ] [ i ] + a [ s − j ] [ j ] dp[s][i][j]=max(dp[s-1][i-1][j-1],dp[s-1][i-1][j],dp[s-1][i][j-1],dp[s-1][i][j])+a[s-i][i]+a[s-j][j] dp[s][i][j]=max(dp[s−1][i−1][j−1],dp[s−1][i−1][j],dp[s−1][i][j−1],dp[s−1][i][j])+a[s−i][i]+a[s−j][j]
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
int n,m,a[55][55];
int dp[110][55][55];
int main()
{
cin>>n>>m;
rep(i,1,n)
rep(j,1,m)
cin>>a[i][j];
mes(dp,-1);
dp[2][1][1]=0;
for(int step=3;step<=n+m-1;++step)
{
for(int i=1;i<m;++i)
{
for(int j=i+1;j<=m;++j)
{
dp[step][i][j]=max(dp[step-1][i-1][j-1],dp[step][i][j]);
dp[step][i][j]=max(dp[step-1][i-1][j],dp[step][i][j]);
dp[step][i][j]=max(dp[step-1][i][j-1],dp[step][i][j]);
dp[step][i][j]=max(dp[step-1][i][j],dp[step][i][j]);
if(dp[step][i][j]==-1)
continue;
dp[step][i][j]+=a[step-i][i]+a[step-j][j];
}
}
}
cout<<dp[n+m-1][m-1][m]<<endl;
return 0;
}
题意:n个人围成一圈传球,从小蛮开始传球,经过m次又传给小蛮的方法数
思路:设dp[i][j]表示经过 j 次传球又回到第 i 个人手中的方案数。从0号开始传球,又回到0号手中,答案就是dp[0][m]
转移方程:
d p [ i ] [ j ] = d p [ ( i − 1 + n ) % n ] [ j − 1 ] + d p [ ( i + 1 ) % n ] [ j − 1 ] dp[i][j]=dp[(i-1+n){\%n}][j-1]+dp[(i+1)\%n][j-1] dp[i][j]=dp[(i−1+n)%n][j−1]+dp[(i+1)%n][j−1]
注意:循环的顺序是不可以调换的。我们先从小到大枚举次数,然后再枚举最终球落在某个人的位置的编号。如果我们先枚举了人的编号,那么在更新dp[i][j]的时候,我们所需要的dp[i+1][j-1]其实是没有做过更新的。其实观察转移方程就能知道,循环的顺序了
#include <iostream>
using namespace std;
int n,m;
int dp[35][35];
int main()
{
cin>>n>>m;
dp[0][0]=1;
for(int j=1;j<=m;++j)
for(int i=0;i<=n-1;++i)
dp[i][j]=dp[(i-1+n)%n][j-1]+dp[(i+1)%n][j-1];
cout<<dp[0][m]<<endl;
return 0;
}