AtCoder Beginner Contest 169 F Knapsack for All Subsets 找规律+(和值一定,求组合数量)动归dp

AtCoder Beginner Contest 169   比赛人数11374  比赛开始后15分钟看到A题,在比赛开始后第20分钟看到所有题

AtCoder Beginner Contest 169  F   Knapsack for All Subsets   找规律+(和值一定,求组合数量)动归dp

总目录详见https://blog.csdn.net/mrcrack/article/details/104454762

在线测评地址https://atcoder.jp/contests/abc169/tasks/abc169_f

解释2^N−1 subsets T of {1,2,…,N}由来

N个数里选1个作为集合对应的集合数量C(N,1)
N个数里选2个作为集合对应的集合数量C(N,2)
N个数里选3个作为集合对应的集合数量C(N,3)
......
N个数里选N个作为集合对应的集合数量C(N,N)

总的数量C(N,1)+C(N,2)+C(N,3)+......+C(N,N)
对照牛顿二项式C(N,0)+C(N,1)+C(N,2)+C(N,3)+......+C(N,N)=2^N
故C(N,1)+C(N,2)+C(N,3)+......+C(N,N)=2^N-1

样例1解释如下

3 4
2 2 4

6

位置1 2 3
数值2 2 4

For each T, the value of f(T) is shown below. The sum of these values is 6
因位置1,数值2!=4
故f({1})=0

因位置2,数值2!=4
故f({2})=0

因位置3,数值4==4
故f({3})=1(One subset {3}satisfies the condition.)

因位置1,数值2;位置2,数值2,2+2==4
故f({1,2})=1({1,2})

因位置3,数值4==4
故f({2,3})=1({3})

因位置3,数值4==4
故f({1,3})=1({3})

因位置1,数值2,位置2,数值2,2+2==4
因位置3,数值4==4
故f({1,2,3})=2({1,2},{3})

样例进一步深入

n=3对应的集合数量

f({1}) {1}({1}出现第1次)
f({2}) {2}
f({3}) {3}

f({1,2}) {1}({1}出现第2次),{2},{1,2}
f({2,3}) {2},{3},{2,3}
f({1,3}) {1}({1}出现第3次),{3},{1,3}


f({1,2,3}) {1}({1}出现第4次),{2},{3},{1,2},{1,3},{2,3},{1,2,3}


可以看到{1},{2},{3}出现频率2^(n-1)=2^(3-1)=4
{1,2},{1,3},{2,3}出现频率2^(n-2)=2^(3-2)=2
{1,2,3}出现频率2^(n-3)=2^(3-3)=1

样例1的进一步模拟

3 4
2 2 4

6

位置1 2 3
数值2 2 4

dp[0]=2^3

读入2
dp[2]=dp[2-2]/2=dp[0]/2=2^2

读入2
dp[4]=dp[4-2]/2=dp[2]/2=2^1

请注意,此刻已构成了2+2==4,对应子集{1,2},出现频率2^(n-2)=2^(3-2)=2

读入4
dp[4]=dp[4-4]/2=dp[0]/2=2^2

请注意,此刻已构成了4==4,对应子集{3},出现频率2^(n-1)=2^(3-1)=2^2=4

故最后结果为2+4=6

为了帮助理解,先写一个,没有模运算参与,不能AC的代码。但是却能使读者很快弄懂该题思路。

以下为帮助理解的代码,题中的3个样例都能通过,但不能AC.

#include <stdio.h>
#define LL long long
#define maxn 3010
LL dp[maxn];
int main(){
	int n,s,x,i,j;
	scanf("%d%d",&n,&s);
	dp[0]=1;
	for(i=1;i<=n;i++)dp[0]*=2;
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		for(j=s;j>=x;j--)
			dp[j]=dp[j]+dp[j-x]/2;
	}
	printf("%lld\n",dp[s]);
	return 0;
}

以下为AC代码

inv2=mod-mod/2此句若看不懂,可看此文数字2的乘法逆元

#include <stdio.h>
#define LL long long
#define maxn 3010
#define mod 998244353
LL dp[maxn];
int main(){
	int n,s,x,i,j,inv2;
	scanf("%d%d",&n,&s);
	inv2=mod-mod/2,dp[0]=1;//inv2表示2的乘法逆元
	for(i=1;i<=n;i++)dp[0]=dp[0]*2%mod;
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		for(j=s;j>=x;j--)
			dp[j]=(dp[j]+dp[j-x]*inv2)%mod;
	}
	printf("%lld\n",dp[s]);
	return 0;
}

类似题目

AtCoder Beginner Contest 159 F Knapsack for All Segments 01背包+一维数组+排列组合

AtCoder Beginner Contest 162 F Select Half 动归dp

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值