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. Fiddler设置代理抓手机包

    启动Fiddler,打开菜单栏中的 Tools > Fiddler Options,打开“Fiddler Options”对话框. 在Fiddler Options”对话框切换到“Connect ...

  2. sell学习

    Linux Shell编程入门 从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Sh ...

  3. cacheManager载入问题

    net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please pr ...

  4. builtroot make menuconfig流程

    本文主要介绍一下,buildroot(buildroot-2018.02.1)的make menuconfig.众所周知,在我们执行menuconfig时,会生成一个图形化界面,然后进行相关的配置.同 ...

  5. Redis-ha(sentinel)

    redis的sendtinel 是用来管理多个redis服务器的 作用 • 监控:监控主从服务器是否运作正常(通过给服务器发送心跳包的方式)    • 提醒:当某个Redis服务器出现异常时,可以通过 ...

  6. HDU 1075 What Are You Talking About (Trie)

    What Are You Talking About Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/204800 K ...

  7. httpd-2.2.22安装

    cgi部分共分三部分:html,cgi和服务器(httpd). 首先介绍httpd安装: 1.  软件包:httpd-2.2.22.tar.gz 系统:3.8.0-44-generic #66~pre ...

  8. OSGI

    OSGi(Open Service Gateway Initiative)技术是面向Java的动态模型系统.OSGi服务平台向Java提供服务,这些服务使Java成为软件集成和软件开发的首选环境.Ja ...

  9. 使用Net.Sockets.TcpListener和Net.Sockets.TcpClient进行图片传输时怎样精确控制接收缓存数组大小

    <span style="font-size:18px;">在dotnet平台Net.Sockets.TcpListener和Net.Sockets.TcpClient ...

  10. Azkaban安装配置

    描述: azkaban主要用于离线计算任务的调度 说明: 此处Azkaban选择版本为:3.52.0,部署方式为Cluster模式,即支持多Executor计算节点,目前默认安装方式选择在同一台机器上 ...