划分大理石(多重背包)

前言

发现自己思路好像和正常人不一样
遂有此篇博客

划分大理石

问题描述

有价值分别为 \(1..6\) 的大理石各 \(a[1..6]\) 块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现。

其中大理石的总数不超过 \(20000\)

输入格式:

输入包含多组数据!

每组数据占一行,包含 \(6\) 个整数,表示 \(a[1]∼a[6]\)

当输入为 \(0  0  0  0  0  0\) 时表示输入结束,且该行无需考虑。

输出格式:

每组数据输出一个结果,每个结果占一行。

如果可以实现则输出 \(Can\),否则输出 \(Can't\)

样例输入:

4 7 4 5 9 1
9 8 1 7 2 4
6 6 8 5 9 2
1 6 6 1 0 7
5 9 3 8 8 4
0 0 0 0 0 0

样例输出:

Can't
Can
Can't
Can't
Can

分析

很显然啊,多重背包,而且不问你方案,这就很nice
板子是这样的

bool f[10005];
memset(f,0,sizeof);
f[0]=1;
for (int i=1;i<=6;++i)
    for (int j=1;j<=a[i];++j)
        for (int k=m;k>=a[i];--k)
            f[k]|=f[k-a[i]];
if (f[sum/2]) ...;
else ...;

显然会 TLE Time Limit Enough

考虑优化

考虑到若前\(i\)种石头能凑出\(sum/2\),只有两种情况:

  1. \(i\)之前, 就已经\(f[j]=true\)
  2. \(i\)之前, 就已经\(f[j-i]=true\)

于是考虑贪心

\(vis[j]\) 表示 \(f[j]\) 在阶段 \(i\) 是为 \(true\) 的情况下至少需要多少块 \(i\) 种石头

则板子的 \(for(j) for(k)\) 循环可以优化1维,直接正序扫面

\(!f[j] \&\& f[j-i] \&\& vis[j-i] ﹤ a[i]\) 才可以转移

于是开心的提交

TLE

为啥呢 被hack了

考虑剪枝

发现如果每个价值的大理石都是 \(2\) 的倍数,那么必能分成两部分价值相等 (感谢oistars提供的想法)

于是AC了

code

Elaina's code
#include<bits/stdc++.h>
using namespace std;
const int N=150000;
#define rd read()
#define int long long
#define inf 0x3f
#define INF 0x3f3f3f3f
#define mst(a,b) memset(a,b,sizeof(a))
#define re register
#define Elaina 0
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
int n,m,a[8],sum,hsum,f[N],vis[N];

main(){
	#ifdef ONLINE_JUDGE
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	#endif
	
	while(1){
		sum=0;
		for(int i=1;i<=6;++i){
			a[i]=rd;
		}
		if(!a[1]&&!a[2]&&!a[3]&&!a[4]&&!a[5]&&!a[6]) break;
		sum=a[1]%2+a[2]%2+a[3]%2+a[4]%2+a[5]%2+a[6]%2;
		if(!sum){
			printf("Can\n");
			continue;
		}
		sum=a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6;
		if(sum%2){
			printf("Can't\n");
			continue;
		}
        mst(f,0);
		f[0]=1;
		hsum=sum/2;
		for(int i=1;i<=6;++i){
			mst(vis,0);
			for(int j=0;j<=hsum;++j){
				if(f[j]&&!f[j+i]&&vis[j]<a[i]){
					f[j+i]=1;
					vis[j+i]=vis[j]+1;
				}
			}
		}
		if(f[hsum]) printf("Can\n");
		else printf("Can't\n");
	}
	return Elaina;
} 

别急,还有后续

又双叒叕被卡了

都看到这了,真的不点个赞吗(>ω<*)

posted @ 2024-06-14 16:45  Elaina_0  阅读(48)  评论(12编辑  收藏  举报
浏览器标题切换
浏览器标题切换end