zoukankan      html  css  js  c++  java
  • LOJ3342 「NOI2020」制作菜品

    (n)种材料,每种有(w_i)单位。要分配给(m)个菜,要求:

    每个菜至多两种材料组成,并且都是整数单位,且总和为(k)

    (sum w_i=mk)

    (nle 500,n-2le mle 5000)


    现在做思考程度都不如一年前,事实证明我真的比上一年菜了。

    发现性质:如果把由两种材料组成的菜看成一条边,那么任意方案都可以调整成无环的方案。没卵用。

    好啦以下才是题解:

    先考虑部分分中的(m=n-1)的情况。首先对(w)从小到大排序。于是有:

    1. (w_1<k)。因为(w_1)不超过平均数(frac{n-1}{n}k),所以小于(k)
    2. (w_1+w_n>k)。反证:如果(w_1+w_nle k),则((n-1)k=sum w_ile k+(n-2)w_n),于是(kle w_n<w_1+w_n),矛盾。

    可证只要满足(m=n-1,sum w_i=(n-1)k,w_i>0),则一定有解:取(w_1,w_n)配对,(w_1)被消耗完,(w_n)不会被消耗完,然后进入同样满足条件的子问题。

    (m>n-1)时:如果(forall i,w_ile k),则此时一定是(m=n,w_i=k),显然有解;否则(exist i,w_i>k),将这个(w_i)变成(w_i-k)(m)减一,还是个同样满足条件的子问题。

    剩下的问题是(m=n-2)的情况:

    充分必要条件:如果能把材料划分成两个集合,使得这两个集合都相当于一个独立的子问题(满足(m=n-1)的,(sum w_i=(n-1)k))。(理解:如果把菜看成边,那么至少会连出两个连通块)

    充分性显然,必要性:归纳,假设把最小的(w_i)消掉。无论怎样,消掉的(n)小于等于消掉的(m)(即(Delta(n-m)le 0),因为不能分成两个独立的子问题,所以不可能有(n-1=m),并且由于是用最小(w_i)和其它的消,不会出现自环,所以更不可能(n>m))于是变成了类似的子问题。

    所以只需要搞个背包,找到集合(S)满足(sum_{iin S}w_i-k=-k)即可。用bitset优化DP。求方案不需要记前驱,因为容易推知从哪里转移过来是有解的。


    using namespace std;
    #include <bits/stdc++.h>
    #define N 505
    #define M 5005
    #define O (N*M)
    #define fi first
    #define se second
    #define mp make_pair
    int n,m,k;
    int w[N];
    struct Ans{
    	int u,v,c;
    } a[M];
    int cnt;
    multiset<pair<int,int> > s;
    int ls[N],nl;
    void work0(){
    	s.clear();
    	for (int i=0;i<nl;++i)
    		s.insert(mp(w[ls[i]],ls[i]));
    	/*
    	printf("ls : ");
    	for (int i=0;i<nl;++i)
    		printf("%d ",ls[i]);
    	printf("
    ");
    	*/
    	while (!s.empty()){
    		multiset<pair<int,int> >::iterator p=s.begin(),q;
    		int x=p->fi,u=p->se;
    		s.erase(p);
    		if (s.size()==0 || x>=k){
    			a[++cnt]={u,0,k};
    			//printf("%d %d %d
    ",u,0,k);
    			x-=k;
    			if (x>0)
    				s.insert(mp(x,u));
    		}
    		else{
    			q=--s.end();
    			int y=q->fi,v=q->se;
    			s.erase(q);
    			a[++cnt]={u,v,x};
    			//printf("%d %d %d
    ",u,v,x);
    			y-=k-x;
    			if (y>0)
    				s.insert(mp(y,v));
    		}
    	}
    }
    void work1(){
    	static bitset<N*M*2> f[N];
    	f[0][0+O]=1;
    	for (int i=1;i<=n;++i)
    		if (w[i]-k>=0)
    			f[i]=f[i-1]|f[i-1]<<w[i]-k;
    		else
    			f[i]=f[i-1]|f[i-1]>>-(w[i]-k);
    	if (f[n][-k+O]==0){
    		printf("-1
    ");
    		return;
    	}
    	nl=0;
    	for (int i=n,x=-k;i>=1;--i)
    		if (!f[i-1][x+O]){
    			x-=w[i]-k;
    			ls[nl++]=i;
    		}
    	work0();
    	static int T[N],nt;
    	nt=0;
    	for (int i=n,j=0;i>=1;--i){
    		for (;j<nl && ls[j]>i;++j);
    		if (j==nl || ls[j]!=i)
    			T[nt++]=i;
    	}
    	memcpy(ls,T,sizeof(int)*nt);
    	nl=nt;
    	work0();
    }		
    int main(){
    	//freopen("in.txt","r",stdin);
    	//freopen("out.txt","w",stdout);
    	freopen("dish.in","r",stdin);
    	freopen("dish.out","w",stdout);
    	int T;
    	scanf("%d",&T);
    	while (T--){
    		scanf("%d%d%d",&n,&m,&k);
    		for (int i=1;i<=n;++i)
    			scanf("%d",&w[i]);
    		cnt=0;
    		if (m==n-2)
    			work1();
    		else{
    			nl=0;
    			for (int i=1;i<=n;++i)
    				ls[nl++]=i;
    			work0();
    		}
    		for (int i=1;i<=cnt;++i)
    			if (a[i].v)
    				printf("%d %d %d %d
    ",a[i].u,a[i].c,a[i].v,k-a[i].c);
    			else
    				printf("%d %d
    ",a[i].u,k);
    		//printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Shell 批量搜索关键词并保存结果到文件中(数组、循环)
    解决tensorflow的Session Exception问题
    解决h5py的FutureWarning问题
    【转】Ubuntu16.04安装WPS
    [Linux] 随机切分文件内容
    [Python] 动态函数调用(通过函数名)
    [Python] dict字典的浅复制与深复制
    基于sklearn进行文本向量化
    Asp.Net MVC SingleServiceResolver类剖析
    Asp.Net MVC 高级特性(附带源码剖析)
  • 原文地址:https://www.cnblogs.com/jz-597/p/14733695.html
Copyright © 2011-2022 走看看