poj刷题存档(2)

poj3373

类型是搜索,不过我是按照题解用DP来做的。

设m串长度为len.从后向前,设dp[i][j]为当前决策到第i位,当前的数对k取模余数是j的情况下,与m串后(len - i + 1)位的数字不同最小个数。

放一下渣程序:

#include<cstdio>
#include<cstring>
char s[200];
int divnum;
int mod[200][10] , dp[200][10001] , next_mod[200][10001] , now_num[200][10001];
int len;
void output(int ins , int mod)
{
	if(ins == len) return;
	printf("%d",now_num[ins][mod]);
	output(ins + 1 , next_mod[ins][mod]);
}
int main()
{
	register int i,j,k;
	while(scanf("%s%d",s,&divnum) != EOF)
	{
		len = strlen(s);
		for(i = 0 ; i <= 9 ; ++i) mod[len - 1][i] = i % divnum;
		for(i = len - 2 ; i >= 0 ; --i)
		{
			for(j = 0 ; j <= 9 ; ++j)
			{
				mod[i][j] = mod[i+1][j] * 10 % divnum;
			}
		}
		memset(dp , -1 , sizeof(dp));
		memset(next_mod , 0 , sizeof(next_mod));
		memset(now_num , 0 , sizeof(now_num));
		dp[len][0] = 0;
        for(i = len - 1 ; i >= 0 ; --i)
		{
			for(j = (i == 0 ? 1 : 0) ; j < 10 ; ++j)
			{
				for(k = 0 ; k < divnum ; ++k)
				{
					if(dp[i+1][k] == -1)
					{
						//dp[i][k] = -1;
						continue;
					}
					int new_mod = (mod[i][j] + k) % divnum;
					if(dp[i][new_mod] == -1 || dp[i][new_mod] > dp[i+1][k] + (j != (s[i] - '0')))
					{
						dp[i][new_mod] = dp[i+1][k] + (j != (s[i] - '0'));
						next_mod[i][new_mod] = k;
						now_num[i][new_mod] = j;
					}
				}
			}
		}
		output(0,0);
		puts("");
	}
	return 0;
}
poj3034

按照时间为阶段划分进行的水dp。不过有几点要特别注意。

你的程序能准确的算出一条线段上的整点吗?(想了一上午)

另外注意这个样例:

20 5 4
1 0 1
0 1 1
0 5 2
1 6 2
0 0 0

你猜他的答案是多少?


是4!


poj3254

状压dp.

这道题是属于一种计数问题。

一般步骤是由一层的某种状态向下一层拓展状态,把下一层每一个拓展出的状态都加上这一层这种状态的数目。

开始时,dp[0][0] = 1,然后从0-n层依次向下拓展,dp[n+1][0]就是答案。

代码:

#include<cstdio>
int map[15][15];
int dp[15][10000];
int n,m;
const int mod = 100000000;
void dfs(int row , int now_state , int next_state , int ins)
{
	if(ins >= m)
	{
		dp[row + 1][next_state] = (dp[row + 1][next_state] + dp[row][now_state]) % mod;
		return;
	}
	if(map[row + 1][ins] && (now_state & (1 << ins)) == 0)
	{
		dfs(row , now_state , next_state | (1 << ins) , ins + 2);
	}
	dfs(row , now_state , next_state , ins + 1);
}
int main()
{
	register int i,j;
	scanf("%d%d",&n,&m);
	for(i = 1 ; i <= n ; ++i)
	for(j = 0 ; j < m ; ++j)
		scanf("%d",&map[i][j]);
	dp[0][0] = 1;
	for(i = 0 ; i <= n ; ++i)
	for(j = 0 ; j < (1 << m) ; ++j)
	{
		if(dp[i][j])
		{
			dfs(i , j , 0 , 0);
		}
	}
	printf("%d",dp[n + 1][0]);
	return 0;
}

poj 2057

暂且跳过。。。


poj1947

树形DP.

依据贝神的教导,这类计数问题有两种思路:

1.根节点把子节点都甩掉,然后一点一点往上加;

2.根节点把子节点都算上,然后一点一点往下减。

dp[i][j]表示以i为根节点的子树保留j个节点(包括i自己)所需的最小切割边数。

所以初始化时把所有的dp[x][1]都清成0.疑问(日前还不理解。。。)

能够有所启发的是。。。

对于每一个子节点,都进行选与不选两种决策。这是一种很好的思路。(其实就是背包)


poj3070

典型的矩阵乘法优化线性递推。

重新理解了一下矩阵乘法,而不是机械的背代码。

a*b(a行b列)的矩阵A和b*c(b行c列)的矩阵B相乘能够得到一个a*c的矩阵C。

其中矩阵A中第i行(共b个元素)由左到右与矩阵B中第j列(共b个元素)由上至下依次相乘的乘积和即为C矩阵第i行第j列的元素。

自己画个图模拟一下就很清楚了。

线性递推的时候就用这种方式设计转移矩阵。

一般n项的线性递推要用到一个n*n的转移矩阵。


光有矩阵乘法是没用的,需要快速幂才能降到log级。

别的不多说了。

代码:

#include<cstdio>
#include<cstring>
const int mod = 10000;
struct matrix
{
	int m,n;
	int d[2][2];
	matrix()
	{
		m = n = 0;
		memset(d , 0 , sizeof(d));
	}
	void operator *= (const matrix &b)
	{
		register int i,j,k;
		matrix c;
		c.m = m , c.n = b.n;
		for(i = 0 ; i < m; ++i)
		for(j = 0 ; j < b.n ; ++j)
		for(k = 0 ; k < n ; ++k)
		{
			c.d[i][j] = (c.d[i][j] + d[i][k] * b.d[k][j]) % mod;
		}
		*this = c;
	}
};
int main()
{
	int s;
	while(scanf("%d",&s) && s != -1)
	{
		matrix m0;
		m0.m = 1 , m0.n = 2;
		m0.d[0][0] = 0 , m0.d[0][1] = 1;
		matrix add;
		add.m = add.n = 2;
		add.d[0][0] = 0 , add.d[0][1] = 1;
		add.d[1][0] = 1 , add.d[1][1] = 1;
		matrix ans;
		ans.m = ans.n = 2;
		ans.d[0][0] = 1 , ans.d[0][1] = 0;
		ans.d[1][0] = 0 , ans.d[1][1] = 1;
		while(s)
		{
			if(s & 1) ans *= add;
			add *= add;
			s >>= 1;
		}
		m0 *= ans;
		printf("%d\n",m0.d[0][0]);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值