有(n)种材料,每种有(w_i)单位。要分配给(m)个菜,要求:
每个菜至多两种材料组成,并且都是整数单位,且总和为(k)。
(sum w_i=mk)
(nle 500,n-2le mle 5000)
现在做思考程度都不如一年前,事实证明我真的比上一年菜了。
发现性质:如果把由两种材料组成的菜看成一条边,那么任意方案都可以调整成无环的方案。没卵用。
好啦以下才是题解:
先考虑部分分中的(m=n-1)的情况。首先对(w)从小到大排序。于是有:
- (w_1<k)。因为(w_1)不超过平均数(frac{n-1}{n}k),所以小于(k)。
- (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;
}