ARC107——D - Number of Multisets

D - Number of Multisets

之前写过一个类似表示的题(2018CCPC吉林赛区——C - Justice),也是让用 1 2 , 1 4 , 1 8 … \frac1 2 ,\frac 14,\frac1 8 \dots 21,41,81凑数,我效仿之前的思路写了个复杂度不清楚的东西(非常爆炸)最终没能过此题

记一下自己写的垃圾代码

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3110;
const int mod=998244353;
int n,k;
int f[20][N][N];
ll dfs(int u,int cnt,int now)
{    
	if(now+cnt>n) return 0;
    if(!cnt) return now==n;
    if(u>12) return 0;
    if(f[u][cnt][now]!=-1) return f[u][cnt][now];
    f[u][cnt][now]=0;
    for(int i=0;i<=cnt;i++)
        f[u][cnt][now]=(f[u][cnt][now]+dfs(u+1,(cnt-i)*2,now+i))%mod;
    return f[u][cnt][now];
}
int main()
{
    //IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        memset(f,-1,sizeof f);
        cin>>n>>k;
        cout<<dfs(1,k,0)<<'\n';
    }
    return 0;
}

这题的正解时划分dp,之前有一个整数划分的题,我用的完全背包做的,一直没用划分dp做,这次遇到题目就不会了hh
大佬题解

状态表示: f ( i , j ) f_{(i,j)} f(i,j)当前需要选出 i i i个数的和时 j j j的方案数
状态转移:我们把方案划分成两种情况:①至少选择一个1。②不选择1

  • 对于①: f ( i , j ) = f ( i − 1 , j − 1 ) f_{(i,j)}=f_{(i-1,j-1)} f(i,j)=f(i1,j1)
  • 对于②:不选择 1 1 1,此时问题转化为:问有多少种选出 i i i 个数的方案,满足 i 个数的和为 j j j 。这 i i i 个数都须是 1 2 i ( i ∈ [ 1 , ∞ ) ) \frac{1}{2^i}(i∈[1,∞)) 2i1(i[1,)) 的形式。我们考虑这个子问题的限制条件与原问题的限制条件的关系。如果我们将这 i i i 个数同时 × 2 ×2 ×2,那么子问题的限制条件就和原问题的限制条件一致了。此时问题转化为求解 f ( i , 2 j ) f_{(i,2j)} f(i,2j)

然后考虑一下边界条件记忆化搜素即可。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=3010;
const int mod=998244353;
int f[N][N];
int n,k;
int dfs(int i,int j)
{
    if(i<=j) return i==j;
    if(!j) return 0;
    if(f[i][j]!=-1) return f[i][j];
    return f[i][j]=(dfs(i-1,j-1)+dfs(i,2*j))%mod;
}
int main()
{
    //IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        memset(f,-1,sizeof f);
        cin>>n>>k;
        cout<<dfs(n,k)<<'\n';
    }
   
    return 0;
}

总结:dp的本质时递推,如果能够设计某种表示能够有一种递推关系,那么有可能该解就可以得出答案。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值