【转】POJ3181——Dollar Dayz(完全背包优化)

看到一篇很棒的题解,存下来。书上一直没明白的看这个题解瞬间打通任督六脉。
原博客地址:http://www.hankcs.com/program/cpp/poj-3181-dollar-dayz.html

原题如下:
Dollar Dayz
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 6032 Accepted: 2273
Description

Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for 1, 2, and 3.FarmerJohnhasexactly 5 to spend. He can buy 5 tools at 1eachor1toolat 3 and an additional 1 tool at $2. Of course, there are other combinations for a total of 5 different ways FJ can spend all his money on tools. Here they are:

    1 @ US$3 + 1 @ US$2

    1 @ US$3 + 2 @ US$1

    1 @ US$2 + 3 @ US$1

    2 @ US$2 + 1 @ US$1

    5 @ US$1

Write a program than will compute the number of ways FJ can spend N dollars (1 <= N <= 1000) at The Cow Store for tools on sale with a cost of 1.. K (1 <= K <= 100).
Input

A single line with two space-separated integers: N and K.
Output

A single line with a single integer that is the number of unique ways FJ can spend his money.
Sample Input

5 3
Sample Output

5

农夫约翰有N元钱,市场上有价值1……K的商品无限个,求所有的花钱方案?
这是一个完全背包问题和大数的混合题目。

《2.3 记录结果再利用的“动态规划” 优化递推关系式》练习题的第三题。

定义

dp[i][j] := 用i种价格配出金额j的方案数。

那么dp[i][0] = 1,使用任何价格配出金额0的方案个数都是1(什么都不用)。

递推关系式:

dp[i][j] = dp[i – 1][j] + dp[i – 1][j – i] + dp[i – 1][j – 2 * i] + … + dp[i – 1][0]

解释一下,dp[i][j] += 所有dp[i – 1][x]之和,其中x为比j小i的整数倍的数。我这种“中文伪码”描述得很别扭,拿用例举个栗子吧:

dp[2][5] = dp[1][5] + dp[1][3] + dp[1][1];
于是初步的代码:

include

using namespace std;

unsigned long long dp[100 + 16][1000 + 16]; // dp[i][j] := 用i种价格配出金额j的方案数

///SubMain//
int main(int argc, char *argv[])
{

ifndef ONLINE_JUDGE

freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

endif

int N, K;
cin >> N >> K;
dp[0][0] = 1;
for (int i = 1; i <= K; ++i)
{
    for (int k = 0; k <= N; k += i)
    {
        for (int j = N; j >= k; --j)
        {
            dp[i][j] += dp[i - 1][j - k];
        }
    }
}
cout << dp[K][N] << endl;

ifndef ONLINE_JUDGE

fclose(stdin);
fclose(stdout);
system("out.txt");

endif

return 0;

}
///End Sub//
上面这份代码提交后报WA,换用 unsigned long long 依然WA,猜测是大数。嘛,那就实现一个简易的大数加法吧,原本用一个 unsigned long long 的地方改用两个,分别表示低位和高位。由于 unsigned long long 最大范围到 1844674407370955161 ,所以取一个比这个值小一个数量级同时又能被10整除的数作为limit,得出下面这份AC代码:

include

using namespace std;

// 0高位 1低位
unsigned long long dp[100 + 16][1000 + 16][2]; // dp[i][j] := 用i种价格配出金额j的方案数

define LIMIT_ULL 100000000000000000

///SubMain//
int main(int argc, char *argv[])
{

ifndef ONLINE_JUDGE

freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

endif

int N, K;
cin >> N >> K;
dp[0][0][1] = 1;
for (int i = 1; i <= K; ++i)
{
    for (int k = 0; k <= N; k += i)
    {
        for (int j = N; j >= k; --j)
        {
            dp[i][j][0] += dp[i - 1][j - k][0];
            dp[i][j][1] += dp[i - 1][j - k][1];
            // 高位进位
            dp[i][j][0] += dp[i][j][1] / LIMIT_ULL;
            // 低位限制
            dp[i][j][1] = dp[i][j][1] % LIMIT_ULL;
        }
    }
}
if (dp[K][N][0])
{
    cout << dp[K][N][0];
}
cout << dp[K][N][1] << endl;

ifndef ONLINE_JUDGE

fclose(stdin);
fclose(stdout);
system("out.txt");

endif

return 0;

}
///End Sub//
12507590 hankcs 3181 Accepted 1856K 500MS C++ 1025B 2014-02-09 20:58:50
不过上面这份代码并非最优的,如果碰上了男人八题那种测试数据的话肯定会TLE吧。

为何说它不是最优的,因为在递推关系式中

dp[i][j] = dp[i – 1][j] + dp[i – 1][j – i] + dp[i – 1][j – 2 * i] + … + dp[i – 1][0]
将j换成j – i有

dp[i][j – i] = dp[i – 1][j – i] + dp[i – 1][j – 2 * i] + … + dp[i – 1][0]
于是就有:

dp[i][j] = dp[i – 1][j] + dp[i][j – i]
于是可以省掉一重循环,将复杂度降低到O(NK)。

……好冷,先去烤个火,指头冻僵了……

好的,优化递推关系式之后的代码:

include

using namespace std;

// 0高位 1低位
unsigned long long dp[100 + 16][1000 + 16][2]; // dp[i][j] := 用i种价格配出金额j的方案数

define LIMIT_ULL 100000000000000000

///SubMain//
int main(int argc, char *argv[])
{

ifndef ONLINE_JUDGE

freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);

endif

int N, K;
cin >> N >> K;
for (int i = 1; i <= K; ++i)
{
    dp[i][0][1] = 1;
    for (int j = 1; j <= N; ++j)
    {
        if (j < i)
        {
            dp[i][j][0] = dp[i - 1][j][0];
            dp[i][j][1] = dp[i - 1][j][1];
        }
        else
        {
            dp[i][j][0] = dp[i - 1][j][0] + dp[i][j - i][0];
            dp[i][j][1] = dp[i - 1][j][1] + dp[i][j - i][1];
            // 高位进位
            dp[i][j][0] += dp[i][j][1] / LIMIT_ULL;
            // 低位限制
            dp[i][j][1] = dp[i][j][1] % LIMIT_ULL;
        }
    }
}

if (dp[K][N][0])
{
    cout << dp[K][N][0];
}
cout << dp[K][N][1] << endl;

ifndef ONLINE_JUDGE

fclose(stdin);
fclose(stdout);
system("out.txt");

endif

return 0;

}
///End Sub//
这份代码速度比上一份快了一个数量级。

城市运行管理的重要性与挑战 城市运行体系是以人为本的服务和经济发展体系,涉及决策、管理和执行三个层次。当前城市运行管理面临城市化快速发展、资源环境制约和社会矛盾突出等挑战。信息技术的发展为城市运行管理提供了重要手段,城市信息化经历了数字化、智能化到智慧化的发展过程。我国城市信息化虽取得进展,但仍处于初级阶段,存在缺乏整体规划、资源浪费和协作效率不高等问题。 智慧城市综合运行管理解决方案 智慧城市运行管理中心(SCOC)是支撑城市运行综合管理的神经中枢,旨在掌控城市运行综合体征,促进服务型政府型。该中心通过全面整合运行资源,服务城市未来发展,提升城市运行水平和突发事件处置效率。中心纵向提升综合职能,横向贯通专业分工,包括综合管理平台、专业管理平台和业务操作平台,覆盖城市交通、公共安全、生态环境等多个领域。 智慧城市综合运行管理平台的结构与功能 智慧城市综合运行管理平台包括决策支持系统、处置系统、基础设施和监测系统。平台通过综合展现系统、综合应急指挥系统、综合运行业务联动系统等,实现城市运行的综合监测和管理。物联网数据采集系统利用网络通讯技术,实现城市物联网设备的高效运行。平台还包含云计算业务支撑系统、城市基础数据库、视频图像云平台等,以支持城市运行管理的各个方面。 智慧城市综合运行管理解决方案的优势 该解决方案具有三个核心优势:首先,它提供了完整的智慧城市视角,不仅仅是指挥中心或数据中心,而是智慧城市的实际载体。其次,它建立了完整的城市运行联动体系,打通业务部门壁垒,形成有机融合的业务联动平台,提升业务处理效率和服务水平。最后,方案凝聚了多年智慧城市建设咨询经验,为城市运行管理提供了成熟的解决方案。 项目实施建议 智慧城市运行管理中心的建设思路和项目实施建议是方案的重要组成部分,旨在指导城市如何有效实施智慧城市运行管理解决方案,以应对城市运行管理的挑战,提升城市管理的智能化和效率。通过这些建议,城市能够更好地规划和实施智慧城市项目,实现可持续发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值