邮资的问题

邮资的问题
Time Limit:1000MS Memory Limit:30000KB
Description

有n种面额不同的邮票,面额分别为C1,C2,C3…..Cn。面额Ci的邮票最多可以取Mi张。请问,用这些邮票,可以贴出多少面额不同的邮资(包括0)。贴邮票时,邮票不必全部使用。

Input

本题有多组数据,第一行为一个整数t,表示有t组测试数据。
每组测试数据的第一行为一个整数n,表示有多少种不同面额的邮票。(1<=N<=10)
第二行有n个整数,分别表示第C1,C2….Cn种不同面额。(1<=Ci<=20)
第三行有n个整数,分别表示面额为Ci的邮票有Mi张。(1<=Mi<=20)

Output

每组数据输出一行,每行一个整数,表示可以贴出多少种不同的面额。

Sample Input

2
2
1 2
2 1
1
2
1

Sample Output

5
2




这道题对input每行数据的解释有点不太好让人理解,我重新解释下。
第一行表示t组测试数据。
每组测试数据的第一行表示有多少种不同面额的邮票。
第二行表示每种邮票的面额。
第三行表示每种面额对应的张数。

这道题是个暴力枚举的DP,其实就是遍历所有可能性。我们假设用f(i)表示第i种面额能够贴出的不同邮资的数量,那么:f(i)就等于f(i-1)的各种不同邮资加上第i种不同的邮资组合。就是枚举出各种可能。
下面给出一个简单易懂的code:
#include <iostream>
#include <vector>
using namespace std;
int visit[5000];//记录新形成的邮资是否已经有了

struct Stamp
{
	int value;//面额
	int number;//数量
	vector<int> kind;//形成的邮资
};


int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		cin >> n;
		Stamp *s = new Stamp[n + 1];
		for (int i = 1; i <= n; ++i)
		{
			cin >> s[i].value;
		}
		for (int i = 1; i <= n; ++i)
		{
			cin >> s[i].number;
		}
		memset(visit, 0, sizeof(visit));
		s[0].kind.push_back(0);
		int counter = 0;
		for (int i = 1; i <= n; ++i)//邮票的种类
		{
			for (int j = 0; j <= s[i].number; ++j)//每种邮资的数量
			{
				for (int k = 0; k < (int)s[i - 1].kind.size(); ++k)//上一种邮票形成的不同邮资的数量
				{
					int sum = s[i - 1].kind[k] + s[i].value * j;
					if (visit[sum] == 0)
					{
						++counter;
						visit[sum] = 1;
					}
					s[i].kind.push_back(sum);
				}
			}
			vector<int>(s[i - 1].kind).swap(s[i - 1].kind);//释放内存
		}
		cout << counter << endl;
		delete[]s;
	}
	return 0;
}

这种写法比较浅显易懂,但是不知道为什么提交总是memory limit exceed,我觉得给的将近30M内存,不至于超吧,况且程序中时刻注意把不用的内存释放掉,没弄明白哪里的问题,也请路过的大神,帮在下看看,请告之,感激不尽。

没办法,只能优化下了,但是代码就比较不好懂,先贴出代码再解释:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int visit[5000];

struct Stamp
{
    int value;
    int number;
};
int main()
{
    int t;
    cin >> t;
    int n;
    while(t--)
    {
        memset(visit,0,sizeof(visit));
        cin >> n;
        Stamp *s = new Stamp[n+1];
        for(int i = 1; i <= n; ++i)
        {
            cin >> s[i].value;
        }
        for(int i = 1; i <= n; ++i)
        {
            cin >> s[i].number;
        }
        visit[0] = 1;
        for(int i = 1; i <= n; ++i)//邮票种类
        {
            for(int j = 1; j <=s[i].number; ++j)//每种邮资的数量
            {
                for(int k = 0; k < 5000; ++k)//该为上一种邮票组成的不同邮资的数量,由于偷懒,放了个较大的数
                {
                    if(visit[k] == i)//只有当前轮的邮资才能计入当前轮计算
                    {
                        int sum = k + s[i].value * j;
                        if(visit[sum] == 0)//产生的新邮资即为下一轮改计算的
                        {
                            visit[sum] = i+1;
                        }
                    }
                }
            }
            for(int x = 0; x < 5000; ++x)//把当前轮没有产生新邮资的邮资全部转移到下一轮,因为下一轮和它组合有可能产生新的邮资
            {
                if(visit[x])
                {
                    visit[x] = i+1;
                }
            }
        }
        int counter = 0;
        for(int i = 0; i < 5000; ++i)//统计数量
        {
            if(visit[i])
            {
                ++counter;
            }
        }
        cout << counter << endl;
        delete[] s;
    }
    return 0;
}

代码里注释也解释了下,如果还不明白,我画个图解释吧:





本轮计算产生的新的邮资为2,3,4。但是题目说,邮资可以为0,因此处于便于计算的方便,j从1开始计算,因此要把上一轮没有产生新邮资的数0,1迁移过来(用虚线框起来),因为在下一轮计算时,这些数还会产生新的数。


有个问题想了很久,在硬件飞速发展的今天,有时候难道非得为了去节省那一点点的时间和内存,去优化出一写别人看不懂的代码,这样做是否真的有意义,毕竟代码是写给别人看的,是用于生产的。。。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值