P6775 [NOI2020] 制作菜品 题解

题目链接:P6775 [NOI2020] 制作菜品

首先题目保证 $ n > m-2 $ 这个条件比较抽象,仔细想一想可以发现除了 $ n=m-2 $ 的部分都是简单的:对于 $ n=m-1 $,可以发现质量最小的原材料质量肯定小于等于 $ k $,而质量最小的原材料质量加上质量最大的原材料质量肯定大于 $ k $(证明是显然的),因此我们只需要每次把最小值取完,最大值补充剩下的部分即可规约到一个 $ (n-1,m-1) $ 的子问题。对于 $ n>m-1 $ 的情况显然有质量最大的原材料至少为 $ k $,直接取走作为一个菜品直到 $ n=m-1 $。

对于 $ n=m-2 $ 的情况我们发现 $ n=3 $ 是无解的。考虑如果一个菜品用了两种原材料,那么这两种原材料就连一条边,显然原图不连通,感性来看因为 $ n=3 $ 是无解的,因此直接一个菜品用一种原材料或者在已经连通的两种原材料再连一条边是不优的,因此最终是把所有原材料分成两个子集 $ S $ 和 $ T $,有 $ \sum_{x\in S} d_x = (|S|-1)k $,另外一个子集 $ T $ 也满足这个条件。带一个子集大小的不好处理,我们让每个 $ d_i $ 减去 $ k $,就是一个普通的背包 dp 了,恢复分配方案直接按 dp 数组保存的状态贪心恢复即可,使用 bitset 优化后时间复杂度 $ O(\frac{n^2k}{w}) $。

代码:

#include<bits/stdc++.h>
#define time(null) chrono::steady_clock::now().time_since_epoch().count()
#define int long long
#define uint unsigned long long
#define debug() cout<<"come here\n"
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define pb push_back
#define Code return
#define by 0
#define MCYYDS ;
using namespace std;
int qpow(int a,int b,int p=INF){int ret=1;while(b){if(b&1)ret=(ret*a)%p;a=(a*a)%p;b>>=1;}return ret;}
inline int read(){int ret=0,f=1;char ch=getchar();while(ch<'0'||ch>'9')f=(ch=='-'?-1:f),ch=getchar();while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+(ch^48),ch=getchar();return ret*f;}
inline void write(int x){if(x<0){putchar('-');write(-x);return ;}if(x>9)write(x/10);putchar((char)(x%10+48));}
inline void writech(int x,char ch){write(x);putchar(ch);}
int T,n,m,k,d[505];
bitset<5000005> dp[505];
vector<int> id1,id2;
bool cmp(int x,int y)
{
	return d[x]<d[y];
}
void spilt(int u,int sum)
{
	if(!u)return ;
	int x=d[u]-k;
	if(sum-x>=0&&dp[u-1][sum-x])
	{
		id1.pb(u);
		spilt(u-1,sum-x);
	}
	else
	{
		id2.pb(u);
		spilt(u-1,sum);
	}
	return ;
}
void solve(vector<int> &id)
{
	sort(id.begin(),id.end(),cmp);
	while(id.size()>1)
	{
		cout<<id[0]<<' '<<d[id[0]]<<' '<<id.back()<<' '<<k-d[id[0]]<<'\n';
		d[id.back()]-=k-d[id[0]];
		for(int i=0;i<id.size()-1;i++)
		{
			id[i]=id[i+1];
		}
		id.pop_back();
		sort(id.begin(),id.end(),cmp);
	}
	return ;
}
signed main()
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);
//	cout.tie(0);
	int T=read();
	while(T--)
	{
		n=read(),m=read(),k=read();
		id1.clear(),id2.clear();
		for(int i=1;i<=n;i++)
		{
			d[i]=read();
		}
		if(m==n-2)
		{
			dp[0].reset();
			dp[0][m*k]=1;
			for(int i=1;i<=n;i++)
			{
				int x=d[i]-k;
				if(x>=0)dp[i]=dp[i-1]|(dp[i-1]<<x);
				else dp[i]=dp[i-1]|(dp[i-1]>>(-x));
			}
			if(!dp[n][m*k-k])
			{
				puts("-1");
				continue;
			}
			spilt(n,m*k-k);
			solve(id1),solve(id2);
		}
		else
		{
			for(int i=1;i<=n;i++)
			{
				id1.pb(i);
			}
			sort(id1.begin(),id1.end(),cmp);
			while(m>=n&&m)
			{
				int x=id1[n-1];
				cout<<x<<' '<<k<<'\n';
				d[x]-=k;
				if(!d[x])
				{
					id1.pop_back();
					n--;
				}
				sort(id1.begin(),id1.end(),cmp);
				m--;
			}
			solve(id1);
		}
	}
	Code by MCYYDS
}

posted @ 2026-10-07 08:51  MCYYDS  阅读(6)  评论(0)    收藏  举报