问题已经解决
可以用这两种方法
1 分组背包
2 多重集组合数
题意:蚂蚁们来自3个家庭,每个家庭内部成员一个样,不同家庭不一样。从里面选s个,能有几种组合,选b个有几种组合。s的组合数加上b的组合数。
我看的题解是分组背包(有些疑问)
先上代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 1005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
int dp[maxn*100];
int num[maxn];
int t,a,s,b;
int main()
{
int i,j,x,k;
while (scanf("%d%d%d%d",&t,&a,&s,&b)!=EOF)
{
memset(num,0,sizeof num);
memset(dp,0,sizeof dp);
for(int i=1;i<=a;i++)
{
scanf("%d",&x);
num[x]++;
}
dp[0]=1;
for(int i=1;i<=t;i++)
{
for(int j=a;j>=0;j--)
{
for(int k=1;k<=num[i];k++)
{
if (j-k>=0)
{
dp[j]=(dp[j]+dp[j-k])%1000000;
}
}
}
}
int ans=0;
for(int i=s;i<=b;i++)
ans=(ans+dp[i])%1000000;
printf("%d\n",ans);
}
return 0;
}
这是AC的,,竟然AC了,,后来我们讨论他的复杂度是接近 1e10 的
1000* 100000 *100
可能数据水。
我们先不管这个了。
接下来是我的疑惑。
a={1,1,1,1,1}
b={2 , 2 , 2 , 2}
c={3 , 3}
从不同的集合里面拿东西组成题目需要的方案
我突然感觉像是多重背包里面的 0—1 背包部分;
包的容量是 总数,绝对大于等于 单种物品(体积都是 1)的数量
接下来是 改成 多重背包里的0—1背包部分
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 1005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
int dp[maxn*100];
int num[maxn];
int t,a,s,b;
int main()
{
int i,j,x,k;
while (scanf("%d%d%d%d",&t,&a,&s,&b)!=EOF)
{
memset(num,0,sizeof num);
memset(dp,0,sizeof dp);
for(int i=1;i<=a;i++)
{
scanf("%d",&x);
num[x]++;
}
dp[0]=1;
for(int i=1;i<=t;i++)
{
for(int k=1;k<=num[i];k++)//在多重背包里这里是有二进制优化的
for(int j=a;j>=0;j--)
{
if (j-k>=0)
{
dp[j]=(dp[j]+dp[j-k])%1000000;
}
}
}
int ans=0;
for(int i=s;i<=b;i++)
ans=(ans+dp[i])%1000000;
printf("%d\n",ans);
}
return 0;
}
这个代码样例都过不了
不明白怎么回事,,希望大佬伸出援助之手,,在此谢过了。
时隔两天,超哥给我讲明白了。(对于背包,理解的不到位鸭)。
先说 0–1 背包, 不论是 单纯 0-1 背包还是 多重背包里面的 0-1 背包,每个物品都是不同的。
但是这个题 是 一个家庭的成员是相同。
对于不同的物品是 先遍历 i 再遍历背包大小 j
所以我 先 i 再 k 再 j 相当于 把相同家庭里面的蚂蚁当成不同的来考虑。
总结:
如果 是不同的物品,先 种类,再 包的容量
相同的物品,先 组别 i ,再 包的容量 j ,再 组内取得数量 k。
多重集组合数的思路
#include<iostream>
#include<cstring>
using namespace std;
int a[100005]={0};
int dp[1005][100005]={0};
int main()
{
int m,n,s,b;
cin>>m>>n>>s>>b;
for(int i=1;i<=n;i++)
{
int q;
cin>>q;
a[q]++;
}
for(int i=0;i<=m;i++)
dp[i][0]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(j-1-a[i]>=0)
dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1-a[i]]+1000000)%1000000;
else
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%1000000;
}
int sum=0;
for(int i=s;i<=b;i++)
sum=(dp[m][i]+sum)%1000000;
cout<<sum<<endl;
return 0;
}
状态转移方程来自《挑战》p68
说完了。
唉呀妈呀,,推了我一下午,费劲。。
推导过程写书上了,,感叹一下思路巧妙。
求和的东西 展开了,再从展开式中拿出 一个dp【i】【j】,后面再凑一个求和的样子。
回到最初的方程,从右向左看,,,