码迷,mamicode.com
首页 > 其他好文 > 详细

poj 3181 Dollar Dayz DP

时间:2016-05-03 09:22:17      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

题意:给你一个n,还有k,求问有多少种数字组合,能够使得数字之和为n,这些数字的范围是1到k。

   如,给你n=4, k=2。那么 1+1+1+1=4, 1+1+2=4,2+2=4,四种组合。

思路:完全背包,可以设d[i][j]代表从i个数字相加和为j的组合数。

   那么,可以考虑把这些组合数分为,有数字i和没有数字i,那么没有数字i的组合数就为d[i-1][j],有数字i的组合数就为d[i][j-i](可以在这些组合里面加上1个i)。

   所以,转移方程可以写成d[i][j] = d[i-1][j] + d[i][j-i](若j<i,那么为0);

   按照这些个方程编码后,输入1000 100(最大值),结果就溢出了,显然这个组合数太大了。

   那么可以考虑试试用longlong,然后把组合数分成前一半和后一半的数字记录。

 

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005;
const long long MOD = 1000000000000000000;
long long a[105][N], b[105][N];
int n,k;
void solve()
{
	memset(b, 0, sizeof(b));
	memset(a, 0, sizeof(a));
	
	for(int i = 0; i <= k; i++)
		b[i][0] = 1;
		
	for(int i = 1; i <= k; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			if(j >= i)
			{
				b[i][j] = (b[i-1][j]+b[i][j-i])%MOD;
				a[i][j] = (a[i-1][j]+a[i][j-i])+(b[i-1][j]+b[i][j-i])/MOD;
			}
			else
			{
				b[i][j] = b[i-1][j];
				a[i][j] = a[i-1][j];
			}
		}

	}
	
	if(a[k][n]>0)
          cout<<a[k][n];
	cout<<b[k][n]<<endl;

}
int main()
{
        //freopen("out.txt", "w", stdout);
	while(~scanf("%d %d", &n, &k))
	{
		solve();
	}
	return 0;
}

  

滚动数组 AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005;
const long long MOD = 1000000000000000000;
long long a[2][N], b[2][N];
int n,k;
void solve()
{
	int f = 1;
	memset(b, 0, sizeof(b));
	memset(a, 0, sizeof(a));
	
	for(int i = 0; i <= 1; i++)
		b[i][0] = 1;
		
	for(int i = 1; i <= k; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			if(j >= i)
			{
				b[f][j] = (b[!f][j]+b[f][j-i])%MOD;
				a[f][j] = (a[!f][j]+a[f][j-i])+(b[!f][j]+b[f][j-i])/MOD;
			}
			else
			{
				b[f][j] = b[!f][j];
				a[f][j] = a[!f][j];
			}
		}
		f = !f;
		b[f][0] = 1;
		for(int j = 1; j <= n; j++)
			b[f][j] = 0;
	}
	
	if(a[!f][n]>0)
        cout<<a[!f][n];
	cout<<b[!f][n]<<endl;

}
int main()
{
        //freopen("out.txt", "w", stdout);
	while(~scanf("%d %d", &n, &k))
	{
		solve();
	}
	return 0;
}

  

做法2:参考网上,基本和第一种做法一样,只是状态转移方程有点不一样。

   方程d[i][j]——整数i的划分个数,划分中最大整数不大于j

   从而分类讨论,若i < j,整数i的划分里,最大整数必定小于等于i,故d[i][j] = d[i][i]。

          若i = j,整数i的划分里,单独i就已为一个划分,所以,整数i的划分可分为单独一个i和没有i的划分,那么d[i][j] = d[i][j-1]+1

          若i > j,整数i的划分可分为有整数j或者没有整数j,从而d[i][j] = d[i][j-1] + d[i-j][j]

做法2 AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1005;
const long long MOD = 1000000000000000000;
long long a[N][105], b[N][105];
int n,k;
void solve()
{
	memset(b, 0, sizeof(b));
	memset(a, 0, sizeof(a));
	
	for(int i = 0; i <= k; i++)
		b[1][i] = 1;
	
	for(int i = 2; i <= n; i++)
	{
		for(int j = 1; j <= k; j++)
		{
			if(i > j)
			{
				b[i][j] = (b[i][j-1] + b[i-j][j])%MOD;
				a[i][j] = a[i][j-1] + a[i-j][j]+(b[i][j-1] + b[i-j][j])/MOD;
			}else if(i == j)
			{
				b[i][j] = (b[i][j-1]+1)%MOD;
				a[i][j] = a[i][j-1] + (b[i][j-1]+1)/MOD;
				//if(b[i][j-1]+1>MOD) a[i][j]++;
			}else
			{
				b[i][j] = b[i][i];
				a[i][j] = a[i][i];
			}
		}
	}
	
	if(a[n][k]>0)
        cout<<a[n][k];
	cout<<b[n][k]<<endl;

}
int main()
{
        //freopen("out.txt", "w", stdout);
	while(~scanf("%d %d", &n, &k))
	{
		solve();
	}
	return 0;
}

  

poj 3181 Dollar Dayz DP

标签:

原文地址:http://www.cnblogs.com/sevenun/p/5453748.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!