poj 1837 Balance 动态规划

题目链接:http://poj.org/problem?id=1837

 

使用迭代器对STL容器进行遍历的方法:

for(set<int>::iterator it = check.begin(); it != check.end(); it++)

{

  //...*it

}

 

 

本题

a[]存挂钩位置

b[]存物品质量

 

把挂在天平左边的物品的质量视为负数 反之为正数

总质量的极限为20件重25的物品都挂在15的天平挂钩处 即7500

dp[i][j]表示前i件物品总质量为(j-10000)时的挂法总数【数组下标不能为负 所以整体往右移10000】

前i件的状态至于前i-1件有关 所以用滚动数组来作dp

 

裸的dp思想如下:

memset(dp, 0, sizeof(dp));//初始化
dp[0][10000] = 0;//初始条件

for i = 0...g
    for k = 0...20000
        if dp[cur][k] != 0 //保证下面的for循环中 dp数组的第二维的下标不为负数
            for j = 0...c
                dp[i][k + b[i]*a[j]] += dp[i-1][k];//把重b[i]的物品放a[j]处后 用原状态dp[i-1][k]的值来不断更新现状态

 

以上过程稍加改动即变为滚动数组实现

复杂度:20 * 30 * 20000  约10^7

 

不同于许多别人的博客

个人认为这只是一道“类01背包”的问题 而不像别人所说的“就是01背包”

 

之前对背包问题的理解有误

看了背包九讲的目录之后才发现

“每种物品最多只能放一次”就可以往01背包想了

个人觉得

背包问题不仅旨在解决求最大价值的问题

它更广泛的意义在于 为关于“选物品”之类的问题提供了一种合适的操作模式

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 25;
const int maxm = 20000;
int dp[2][maxm];

int a[maxn];
int b[maxn];

int main()
{
    //freopen("in.txt", "r", stdin);

    int c, g;
    while(scanf("%d%d", &c, &g) == 2)
    {
        for(int i = 0; i < c; i++)
            scanf("%d", &a[i]);
        for(int i = 0; i < g; i++)
            scanf("%d", &b[i]);

        int cur = 0;
        dp[cur][10000] = 1;

        for(int i = 0; i < g; i++)
        {
            memset(dp[cur^1], 0, sizeof(dp[cur]));
            for(int t = 0; t < 20000; t++)
            {
                if(dp[cur][t])
                {
                    for(int j = 0; j < c; j++)
                    {
                        int tmp = t + b[i]*a[j];

                        dp[cur^1][tmp] += dp[cur][t];

                    }
                }
            }
            cur = cur ^ 1;
        }

        printf("%d\n", dp[cur][10000]);

    }



    return 0;
}

 

最后我想吐槽一个事

stl要慎用

本来想着在枚举20000个总质量的时候用set记录出现过的总质量 来做个优化

没想到时间反而变成原来的50倍!

 

感觉stl在写大模拟的时候好用

然后像这种题还是少用吧

 

上面的代码16MS 下面的代码750MS

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 25;
const int maxm = 20000;
int dp[2][maxm];

int a[maxn];
int b[maxn];
set<int> check[2];

int main()
{
    //freopen("in.txt", "r", stdin);

    int c, g;
    while(scanf("%d%d", &c, &g) == 2)
    {
        check[0].clear();
        check[1].clear();

        for(int i = 0; i < c; i++)
            scanf("%d", &a[i]);
        for(int i = 0; i < g; i++)
            scanf("%d", &b[i]);

        int cur = 0;
        dp[cur][10000] = 1;
        check[cur].insert(10000);

        for(int i = 0; i < g; i++)
        {
            memset(dp[cur^1], 0, sizeof(dp[cur]));
            for(set<int>::iterator it = check[cur].begin(); it != check[cur].end(); it++)
            {
                for(int j = 0; j < c; j++)
                {
                    int tmp = *it + b[i]*a[j];

                    dp[cur^1][tmp] += dp[cur][*it];

                    check[cur^1].insert(tmp);
                }


            }
            check[cur].clear();
            cur = cur ^ 1;
        }

        printf("%d\n", dp[cur][10000]);

    }



    return 0;
}

 

转载于:https://www.cnblogs.com/dishu/p/4293353.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值