P6775 [NOI2020] 制作菜品(dp,bitset)

传送门


解题思路

m:菜数 n:原料数

  • 当 m=n-1 时:最小的跟最大的两两结合。

  • 当 m>=n 时:最大的单独做,最后就变成了 m=n-1 的情况。

  • 当 m=n-2 时:将每个物品减去k后做01可行性背包,可以使用 bitset 优化。

具体证明可以看这里:题解 P6775 【[NOI2020]制作菜品】

如何输出路径?

开n个bitset,第i个表示用前i个物品,然后倒序枚举物品,能转移就转移。

注意快读和关掉同步不能一块用,否则死循环会T掉?

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<bitset>
using namespace std;
template<class T>inline void read(T &x){
	cin>>x;
}
const int maxn=2500000;
bitset<maxn*2> dp[505];
int a[505],vis[505],n,m,k;
struct node{
	int v,id;
}d[505];
bool cmp1(node a,node b){
	return a.v<b.v;
}
void work1(int n){
	for(int i=1;i<n;i++){
		int minn=0,maxx=0;
		for(int j=1;j<=n;j++){
			if(d[a[j]].v==0) continue;
			if(minn==0||d[a[j]].v<d[minn].v) minn=a[j];
			if(maxx==0||d[a[j]].v>=d[maxx].v) maxx=a[j];
		}
		if(d[minn].v==k){
			cout<<d[minn].id<<' '<<k<<endl;
			d[minn].v=0;
		}else{
			cout<<d[minn].id<<' '<<d[minn].v<<' '<<d[maxx].id<<' '<<k-d[minn].v<<endl;
			d[maxx].v-=k-d[minn].v;
			d[minn].v=0;
		}
	}
}
bool DP(int n){
	dp[0][maxn]=1; 
	for(int i=1;i<=n;i++){
		if(d[i].v>=k) dp[i]=dp[i-1]|(dp[i-1]<<(d[i].v-k));
		else dp[i]=dp[i-1]|(dp[i-1]>>(-d[i].v+k));
	}
	if(!dp[n][maxn-k]) return 0;
	int now=maxn-k;
	for(int i=n;i>=1;i--){
		if(dp[i-1][now-(d[i].v-k)]){
			vis[i]=1;
			now-=d[i].v-k;
		}
	}
	return 1;
}
int main(){
	ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--){
		memset(d,0,sizeof(d));
		memset(vis,0,sizeof(vis));
		read(n);read(m);read(k);
		for(int i=0;i<=n;i++) dp[i].reset();
		for(int i=1;i<=n;i++) read(d[i].v),d[i].id=i;
		sort(d+1,d+n+1,cmp1);
		if(m==n-1){
			for(int i=1;i<=n;i++) a[i]=i;
			work1(n);
		}else if(m>=n){
			int now=n;
			while(m!=n-1){
				if(d[now].v>k) d[now].v-=k;
				cout<<d[now].id<<' '<<k<<endl;
				if(d[now].v<=k) now--;
				m--;
			}
			sort(d+1,d+n+1,cmp1);
			for(int i=1;i<=n;i++) a[i]=i;
			work1(n);
		}else{
			if(!DP(n)){
				cout<<-1<<endl;
				continue;
			}
			int cnt=0;
			for(int i=1;i<=n;i++){
				if(vis[i]){
					a[++cnt]=i;
				}
			}
			work1(cnt);
			cnt=0;
			for(int i=1;i<=n;i++){
				if(!vis[i]){
					a[++cnt]=i;
				}
			}
			work1(cnt);
		}
	}
	return 0;
}

//NOI2020 Day2T1

posted @ 2021-10-15 15:15  尹昱钦  阅读(38)  评论(0编辑  收藏  举报