已完成今日 你真的会 01 背包吗?(https://blog.moeebius.top/index.php/archives/15/) 大学习。

给定 个整数 ,问是否存在一个子集 使得

看到一篇论文,但是感觉都太神秘了。


直接做 ,可以除

找到一个最长前缀 使得 ,先把这个前缀全部选上,和为 。然后每次操作可以从 中删一个数,或加入 中一个数,使得目前集合的

随机打乱可以做到

表示还没考虑 ,目前的和为 是否可行。交换状态值域,记 表示右端点决定到 ,和为 ,左端点最大多少。

转移可以是: 右移一位,选/不选这个物品; 左移到 之间并删掉

复杂度

代码有了。Q7403 Subset Sum

void work(){
	n=read();c=read();
	for(int i=1;i<=n;i++)a[i]=read();a[n+1]=0;
	int p=-1,sum=0;for(int i=1;i<=n;i++){
		if(sum+a[i]>c){p=i-1;break;}
		sum+=a[i];
	}
	if(p==-1){printf("%lld\n",sum);return ;}
	// mems(f,0);
	// f[p+1][p][sum-c+B]=1;
	// for(int i=p+1;i;i--){
		// for(int j=p;j<=n;j++){
			// for(int k=-B;k<=B;k++)if(f[i][j][k+B]){
				// f[i][j+1][k+B]=1;
				// f[i-1][j][k+B]=1;
				// if(k<=0)f[i][j+1][k+a[j+1]+B]=1;
				// elsef[i-1][j][k-a[i-1]+B]=1;
			// }
		// }
	// }
	for(int o=0;o<2;o++)for(int k=-B;k<=B;k++)g[o][k+B]=0;
	g[p&1][sum-c+B]=p+1;
	for(int j=p+1;j<=n;j++){
		for(int k=-B;k<=B;k++)g[j&1][k+B]=g[(j-1)&1][k+B];
		for(int k=-B;k<=0;k++)chkmx(g[j&1][k+a[j]+B],g[(j-1)&1][k+B]);
		for(int k=B;k;k--){
			for(int i=g[(j-1)&1][k+B];i<g[j&1][k+B];i++)chkmx(g[j&1][k-a[i]+B],i);
		}
	}
	for(int i=0;i>=-B;i--)if(g[n&1][i+B]){printf("%lld\n",c+i);break;}
}