51nod 1503 猪和回文 (dp + 优化好题)

11 篇文章 0 订阅
10 篇文章 0 订阅

题目链接:点击打开链接

这道题是个dp计数问题,朴素的dp方程其实并不是很难

dp[x1][y1][x2][y2]表示当从左上角走到x1,y1 从右下角走到x2,y2时的状态总数,然后根据上下左右,字符是否相等转移即可。

但是这里的4维都是高达500的,首先这么大的DP数组肯定是开不下的,所以一定要优化。

思考转移过程,因为这里要求是回文,所以我们肯定是从 两个相同字符长度 转移到 下一个相同字符长度。

所以事实上在计算每一步的时候,不用这么多状态。

所以考虑,按照step把所有的格子编号存储,vector<int> save[step][0/1]表示从 左上角/右下角 走过step步所能达到的格子集合。这样dp数组的空间就省下来了,注意滚动优化。

还有个优化,在代码的注释里。

强烈推荐去CF交一下,地址在这里 :点击打开链接  51nod上数据有点水

#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<vector>
#include<set>
#define LL long long int
using namespace std;

const int mod = 1000000007;
int n,m;
char grid[600][600];
vector<int> save[1200][2];
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int dp[2][1200][1200];
int f[600*600][2];

void liinit(int step,int rev)
{
	for(int i = 0;i<save[step][0].size();i++)
	{
		for(int j = 0;j<save[step][1].size();j++)
		{
			dp[rev][i][j] = 0;
		}
	}
}

void init()
{
	for(int i= 0;i<1200;i++)
	{
		for(int j = 0;j<2;j++) save[i][j].clear();
	}
	memset(dp,0,sizeof(dp));
	memset(f,0,sizeof(f));
}



int main()
{
	while(cin>>n>>m)
	{
		init();
		for(int i = 0;i<n;i++)
		{
			scanf("%s",grid[i]);
		}
		for(int i = 0;i<n;i++)
		{
			for(int j = 0;j<m;j++)
			{
				save[i+j][0].push_back(i*m+j);
				save[abs(n-1-i)+abs(m-1-j)][1].push_back(i*m+j);
				f[i*m+j][0] = save[i+j][0].size()-1;
				f[i*m+j][1] = save[abs(n-1-i)+abs(m-1-j)][1].size()-1;
			}
		}
		for(int i = 0;i<=(n-1+m-1)/2;i++)
		{
			sort(save[i][0].begin(),save[i][0].end());
			sort(save[i][1].begin(),save[i][1].end());
		}
		
		int mode = ( (n - 1 + m - 1)%2 == 0?0:1) ;
		int rev = 0;
		
		int ans = 0;
		if(grid[0][0] == grid[n-1][m-1])
		{
			dp[rev][0][0] = 1;
		}
		else 
		{
			printf("0\n");
			continue;
		}
		for(int step = 0;step <= (n-1+m-1)/2;step++)
		{
			liinit(step+1,rev^1);
			bool finish = false;
			for(int i = 0;i<save[step][0].size();i++)
			{
				int s1 = save[step][0][i];
				for(int j = 0;j<save[step][1].size();j++)
				{
					int s2 = save[step][1][j];
					int nx1 = s1/m;
					int ny1 = s1%m;
					int nx2 = s2/m;
					int ny2 = s2%m;
					if(mode == 0)
					{
						if(s1 == s2)
						{
							ans += dp[rev][i][j];
							ans %= mod;
							finish = 1;
						}
					}
					else if(mode == 1)
					{
						if( (abs(nx1-nx2)+abs(ny1-ny2) == 1) && grid[nx1][ny1] == grid[nx2][ny2] )
						{
							ans += dp[rev][i][j];
							ans %= mod;
							finish = 1;
						}
					}
					
					for(int ii = 0;ii<2; ii++)
					{
						int tx1 = nx1 + dir[ii][0];
						int ty1 = ny1 + dir[ii][1];
						if(tx1 < 0 || tx1 >= n || ty1 < 0 || ty1 >= m) continue;
						for(int jj = 2;jj<4;jj ++)
						{
							int tx2 = nx2 + dir[jj][0];
							int ty2 = ny2 + dir[jj][1]; 
							if(tx2 < 0 || tx2 >= n || ty2 < 0 || ty2 >= m) continue;
							if(grid[tx1][ty1] != grid[tx2][ty2] ) continue;
							//vector<int> ::iterator iter1,iter2;
							//iter1 = lower_bound(save[step+1][0].begin(),save[step+1][0].end(),tx1*m+ty1);
							//iter2 = lower_bound(save[step+1][1].begin(),save[step+1][1].end(),tx2*m+ty2);
							//int idx1 = iter1 - save[step+1][0].begin();
							//int idx2 = iter2 - save[step+1][1].begin();
							
							/*这里原本是使用的二分查找,
							但这里可以使用一个函数f
							f[x][0]表示,从左上角出发,走到x这个位置,这个存在save里所在的下标
							这样可以优化掉一个log,在51nod上就可以过了
							f[x][1]同理了
							*/ 
							int idx1 = f[tx1*m+ty1][0];
							int idx2 = f[tx2*m+ty2][1];
							dp[rev^1][idx1][idx2] += dp[rev][i][j];
							dp[rev^1][idx1][idx2] %= mod;
						}
					}
				}
			}
			if(finish == 1) break;
			rev ^= 1;
		}
		printf("%d\n",ans);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值