邮资的问题
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
有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迁移过来(用虚线框起来),因为在下一轮计算时,这些数还会产生新的数。
有个问题想了很久,在硬件飞速发展的今天,有时候难道非得为了去节省那一点点的时间和内存,去优化出一写别人看不懂的代码,这样做是否真的有意义,毕竟代码是写给别人看的,是用于生产的。。。