题解:P2409 Y的积木

现存的题解似乎没有用 dp 做的,来补一发。

Solution P2409

Idea

我们设 dpi,jdp_{i,j} 为现在考虑到了第 ii 盒积木,总重量为 jj 的方案数。

那么很显然有 dpi,j=dpi1,jai,k+ai,kdp_{i,j}=dp_{i-1,j-a_{i,k}}+a_{i,k},其中 kk 表示这一盒积木中的第几块,ai,ka_{i,k} 表示第 ii 盒第 kk 块积木的重量。

最后统计答案的时候,从小到大枚举,对于每一个可行方案让计数器加一,然后判断是否比 kk 大即可。如果这一段比较抽象,文末会放统计答案的代码。

这样转移的代码就长这样:

dp[0][0]=1;
for(int i=1;i<=n;i++){
	for(int j=1;j<=m[i];j++){
    	for(int k=a[i][j];k<=maxw;k++){
        	dp[i][k]=dp[i][k]+dp[i-1][k-a[i][j]];
        }
    }
}

但是它会 WA 掉最后一组 hack 数据。

我们考虑一下,当 n=100n=100 且全部 mi=100m_i=100 时,若所有重量全部相同,则会出现方案数过多而爆 long long 的情况。

那么我们想,最多会有 kk 个答案,那么我们只需要把 dpi,jdp_{i,j} 保存到最大值为 kk,就可以满足所有情况了。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=105,maxw=10000;
int n,m[N],a[N][N],kk;
int dp[N][N*N];
int main(){
	scanf("%d%d",&n,&kk);
	for(int i=1;i<=n;i++){
		scanf("%d",&m[i]);
		for(int j=1;j<=m[i];j++){
			scanf("%d",&a[i][j]);
		}
	}
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m[i];j++){
			for(int k=a[i][j];k<=maxw;k++){
				dp[i][k]=min(kk,dp[i][k]+dp[i-1][k-a[i][j]]);//最核心的部分:保存到最大值为 k(代码中为 kk)。
			}
		}
	}
	int cnt=0;
	for(int i=0;i<=maxw;i++){
		for(int j=1;j<=dp[n][i];j++){
			cnt++;
			printf("%d ",i);
			if(cnt==kk)return 0;
		}
	}
	return 0;
}

Time

显然转移是 O(n×m×w)\operatorname{O}(n\times m\times w) 的(其中 ww 为最大重量)。最大重量显然是 n×ain\times a_i 的最大值,这个值是 1000010000

所以不会超时,但是复杂度也很高。

posted @   Weslie_qwq  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示