POJ1742

http://poj.org/problem?id=1742

题意

有n种面额的硬币,面额个数分别为Ai、Ci,求最多能搭配出几种不超过m的金额?

思路

据说这是传说中的男人8题呢,对时间和空间复杂度要求都挺高的。

朴素DP三重循环比较容易想到,但显而易见会TLE。

这里由于dp数组记录的是一个bool值(是否能搭配出某金额),记录的信息较少,因而存在浪费。优化思路是dp[i][j]记录用前i种数加和得到j时第i种数最多能剩余多少个(不能加和得到的情况下为-1)。但二维dp数组的空间复杂度仍然没有降下来,这时候会MLE。

改成一维数组几个AC。

关于思路更详细的说明见《挑战程序设计竞赛》第62-64页。

另外可参见另一篇博客,对于这道题解释的非常清楚:POJ1742详解

代码

Source Code

Problem: 1742       User: liangrx06
Memory: 564K Time: 1907MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std; const int N = 100;
const int M = 100000; int main(void)
{
int n, m;
int a[N], c[N];
int dp[M+1]; while (scanf("%d%d", &n, &m) != EOF) {
if (!n && !m) break;
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
for (int i = 0; i < n; i ++)
scanf("%d", &c[i]); fill(dp, dp+m+1, -1);
dp[0] = 0;
for (int i = 0; i < n; i ++) {
for (int j = 0; j <= m; j ++) {
if (dp[j] >= 0)
dp[j] = c[i];
else if (j < a[i] || dp[j-a[i]] <= 0)
dp[j] = -1;
else
dp[j] = dp[j-a[i]] - 1;
}
} int ans = 0;
for (int i = 1; i <= m; i ++) {
if (dp[i] >= 0) ans++;
}
printf("%d\n", ans);
} return 0;
}

POJ3046

http://poj.org/problem?id=3046

题意

有A只蚂蚁,来自T个家族。同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同。任取n只蚂蚁(S<=n<=B),求能组成几种集合?

还可以理解为:

给出T种数字。每种各有N[i]个,然后用这些数字构成一些序列, 问x长度到y长度的序列有多少种?

思路

动态规划题,dp[i][j] 表示前i种数字构成长度为j的序列有多少种。递推关系为:

dp[i][j] = sigma(dp[i - 1][j - k]) k的范围是0~N[i]

注意到这里的sigma(dp[i - 1][j - k]) 可以用部分和算一下。

然后因为总共的数字个数可能有10W个,有1000种数组,会MLE。所以需要开滚动数组来搞。

复杂度的话 是 O(sigma(num[i] * (T + 1 - i))),最坏是1亿。

代码

Source Code

Problem: 3046       User: liangrx06
Memory: 1028K Time: 125MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int T = 1000;
const int A = T*100;
const int MOD = 1000000; int main(void)
{
int t, a, s, b;
int fam[T]; cin >> t >> a >> s >> b;
memset(fam, 0, sizeof(fam));
int num;
for (int i = 0; i < a; i ++) {
scanf("%d", &num);
fam[num-1] ++;
} int dp[2][A+1];
memset(dp, 0, sizeof(dp));
for (int j = 0; j <= b; j ++) {
if (j <= fam[0]) dp[0][j] = 1;
}
for (int i = 1; i < t; i ++) {
for (int j = 0; j <= b; j ++) {
dp[i&1][j] = 0;
for (int k = 0; k <= j && k <= fam[i]; k ++) {
dp[i&1][j] += dp[(i-1)&1][j-k];
}
dp[i&1][j] %= MOD;
}
}
int ans = 0;
for (int j = s; j <= b; j ++)
ans += dp[(t-1)&1][j];
printf("%d\n", ans % MOD); return 0;
}

POJ3181

http://poj.org/problem?id=3181

题意

输入n,和k,问将n用1到k这k个数字进行拆分,有多少种拆分方法。例如:

n=5,k=3 则有n=3+2,n=3+1+1,n=2+1+1+1,n=2+2+1,n=1+1+1+1+1这5种拆分方法

思路

很明显是完全背包问题,假如用dp[i][j]表示考虑到用数1-i拼接成数字j的拼接方法,可以得到状态转移方程如下:

dp[i][j]=sigma(dp[i-1][j-k*i])(k>=0,且j-k*i>=0)

这个题的答案比较大,超过了long long的表示范围,但其实答案的范围可以大致试出来,两个long long拼起来足够表示。

另外二维数组显然是空间浪费的,用滚动数组处理可以将内存空间降维。甚至可以用一维数组表示。

最后,累加实际上是不必要的,还有更进一步的优化思路:

其实可以转到dp[i][j]的状态有两种,一种是dp[i-1][j]就是不用i这个数字拼接成j这个数字的方法数,另一种是dp[i][j-i]就是用了i这个数字拼接的到j-i的方法数。

那么状态转移方程就可以写成:

dp[i][j]=dp[i-1][j]+dp[i][j-i]

做本题的过程中参考了这篇博文:POJ 3181 Dollar Dayz (完全背包)

代码1(累加DP)

Source Code

Problem: 3181       User: liangrx06
Memory: 264K Time: 47MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int K = 100;
const int N = 1000; int main(void)
{
int n, k; cin >> n >> k; long long INF = 1;
for (int i = 0; i < 18; i++)
INF *= 10; long long dp[2][N+1][2];
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
dp[0][0][1] = 0;
for (int i = 1; i <= k; i ++) {
for (int j = 0; j <= n; j ++) {
dp[i&1][j][0] = dp[i&1][j][1] = 0;
for (int k = 0; k <= j; k += i) {
dp[i&1][j][0] += dp[(i-1)&1][j-k][0];
dp[i&1][j][1] += dp[(i-1)&1][j-k][1];
if (dp[i&1][j][0] >= INF) {
dp[i&1][j][0] -= INF;
dp[i&1][j][1] += 1;
}
}
}
}
if (dp[k&1][n][1])
printf("%lld", dp[k&1][n][1]);
printf("%lld\n", dp[k&1][n][0]); return 0;
}

代码2(优化DP,时间复杂度降维)

Source Code

Problem: 3181       User: liangrx06
Memory: 264K Time: 16MS
Language: C++ Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int K = 100;
const int N = 1000; int main(void)
{
int n, k; cin >> n >> k; long long INF = 1;
for (int i = 0; i < 18; i++)
INF *= 10; long long dp[2][N+1][2];
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
dp[0][0][1] = 0;
for (int i = 1; i <= k; i ++) {
for (int j = 0; j <= n; j ++) {
dp[i&1][j][0] = dp[(i-1)&1][j][0] + ((j-i >= 0) ? dp[i&1][j-i][0] : 0);
dp[i&1][j][1] = dp[(i-1)&1][j][1] + ((j-i >= 0) ? dp[i&1][j-i][1] : 0);
if (dp[i&1][j][0] >= INF) {
dp[i&1][j][0] -= INF;
dp[i&1][j][1] += 1;
}
}
}
if (dp[k&1][n][1])
printf("%lld", dp[k&1][n][1]);
printf("%lld\n", dp[k&1][n][0]); return 0;
}

《挑战程序设计竞赛》2.3 动态规划-优化递推 POJ1742 3046 3181的更多相关文章

  1. 挑战程序设计竞赛》P345 观看计划

                                                 <挑战程序设计竞赛>P345 观看计划 题意:一周一共有M个单位的时间.一共有N部动画在每周si时 ...

  2. Aizu 2249Road Construction 单源最短路变形《挑战程序设计竞赛》模板题

    King Mercer is the king of ACM kingdom. There are one capital and some cities in his kingdom. Amazin ...

  3. POJ 2386 Lake Counting 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=2386 <挑战程序设计竞赛>习题 题目描述Description Due to recent rains, water has ...

  4. hdu6078[优化递推过程] 2017多校4

    这道题一眼看过去好像和最长公共子序列有点像. 一开始只想到暴力的推法, 令dp[i][j][k]表示 a[i]=b[j](即以ai,bj为结尾的波浪序列的方案数), 且最终状态为k(0,1分别代表下降 ...

  5. HDU 5863 cjj's string game (矩阵乘法优化递推)

    题目大意:用k种字符构建两个长度为n的字符串(每种字符有无限多个),要求对应位置字符相同的连续子串最长长度为m,问方法数. 其中k,n,m是输入,n(1<=n<=1000000000), ...

  6. poj 3253 Fence Repair 贪心 最小堆 题解《挑战程序设计竞赛》

    地址 http://poj.org/problem?id=3253 题解 本题是<挑战程序设计>一书的例题 根据树中描述 所有切割的代价 可以形成一颗二叉树 而最后的代价总和是与子节点和深 ...

  7. 洛谷 P1464 Function【动态规划(递推)/记忆化搜索(递归)】

    题目描述 对于一个递归函数w(a,b,c) 如果a<=0 or b<=0 or c<=0就返回值1. 如果a>20 or b>20 or c>20就返回w(20,2 ...

  8. 《挑战程序设计竞赛》2.3 动态规划-基础 POJ3176 2229 2385 3616 3280

    POJ3176 Cow Bowling 题意 输入一个n层的三角形,第i层有i个数,求从第1层到第n层的所有路线中,权值之和最大的路线. 规定:第i层的某个数只能连线走到第i+1层中与它位置相邻的两个 ...

  9. 《挑战程序设计竞赛》2.3 动态规划-进阶 POJ1065 1631 3666 2392 2184(5)

    POJ1065: Description There is a pile of n wooden sticks. The length and weight of each stick are kno ...

随机推荐

  1. C#线程同步技术(二) Interlocked 类

    接昨天谈及的线程同步问题,今天介绍一个比较简单的类,Interlocked.它提供了以线程安全的方式递增.递减.交换和读取值的方法. 它的特点是: 1.相对于其他线程同步技术,速度会快很多. 2.只能 ...

  2. EHCache 在WEB项目的使用方法

    EHCache 是一个纯java的在进程中的缓存,它具有下面特性:高速,简单,为Hibernate2.1充当可插入的缓存,最小的依赖性,全面的文档和測试.官方站点http://ehcache.sour ...

  3. 关闭了的SQL Server服务如何打开

    在cmd里输入如下:net start mssqlserver

  4. file's owner以及outlet与连线的理解

    转自:http://www.cnblogs.com/martin1009/archive/2012/06/01/2531028.html xib文件本身可以看做是一个xml,app启动的时候会根据xm ...

  5. 热烈祝贺阿尔法Go首战告捷

    这是人类的一大杰作和进步.一个国家和民族的未来在科技,靠造房子是成不了科技强国的. 当然,也要祝贺一下北上深房价突破历史高位.这也是伟大而不朽的成果.

  6. CentOS6.2下安装中文输入法

    因为在程序中需要输入中文,但是系统没有预装中文输入法,所以就安装一下,顺便记录 1.用root登录 ,或su root2.yum install "@Chinese Support" ...

  7. Oracle与Mysql插入多行数据

    Oracle 一. insert allinto students values ('b10050501','zl')into students values ('b10050502','zjw')s ...

  8. atn 编译java提示资源不足

    今天用ant编译项目出现 [javac] 系统资源不足.的错误,如下 Buildfile: build.xml -compile:    [javac] Compiling 919 source fi ...

  9. hdu6003 Problem Buyer 贪心 给定n个区间,以及m个数,求从n个区间中任意选k个区间,满足m个数都能在k个区间中找到一个包含它的区间,如果一个区间包含了x,那么 该区间不能再去包含另一个数,即k>=m。求最小的k。如果不存在这样的k,输出“IMPOSSIBLE!”。

    /** 题目:hdu6003 Problem Buyer 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6003 题意:给定n个区间,以及m个数,求从n个区 ...

  10. android 学习之rxjava

    最近在做的一个项目,所用到的技术和rxjava有关,因此还特地的学习了下,在这简单的介绍下rxjava: RxJava是什么 RxJava 在 GitHub 主页上的自我介绍是 "a lib ...