G - Ant Counting--(DP)

问题已经解决

可以用这两种方法
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】,后面再凑一个求和的样子。
回到最初的方程,从右向左看,,,

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值