P6775 [NOI2020] 制作菜品
链接
题解
分三种情况讨论:
1.m=n-1,就是alias method。。(NOI考的算法越来越怪啦QAQ),这里是(O(n))的。
2.m>=n,这个时候一定存在某些材料不小于K克,强行用这个材料减K,这样m减少1,n可能不变可能减1。不断重复就能做完或者变成1情况。(O(n))
3.m=n-2,有解的情况当且仅当可以分成两个1情况。可以(O(n^2k))DP,用bitset优化就能通过这题了。
(Code)
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
const int M=5005*505;
int n,m,K;
struct Node{
int id;
int V;
}a[N];
int v[N];
int cnt;
struct ANS{
int idx,Vx;
int idy,Vy;
}b[N];
vector<Node> c,d;
void sol(vector<Node> A){
c.clear();d.clear();
for(int i=0;i<A.size();++i){
if(A[i].V>=K) d.push_back(A[i]);
else c.push_back(A[i]);
}
int i=0,j=0;Node tmp;
while(i<c.size()){
tmp=c[i];++i;
++cnt;b[cnt].idx=tmp.id;b[cnt].Vx=tmp.V;
if(j<d.size()){
b[cnt].idy=d[j].id;b[cnt].Vy=K-tmp.V;
d[j].V-=b[cnt].Vy;
if(d[j].V<K){
c.push_back(d[j]);
++j;
}
}
else{
b[cnt].idy=c[i].id;b[cnt].Vy=K-tmp.V;
c[i].V-=b[cnt].Vy;
if(c[i].V==0) ++i;
}
}
return;
}
bitset<M+M+1> f[505];
vector<Node> q1,q2;
void MAIN(){
scanf("%d%d%d",&n,&m,&K);
for(int i=1;i<=n;++i) {
a[i].id=i;
scanf("%d",&a[i].V);
}
cnt=0;
for(int i=1;i<=m;++i){
b[i].idx=b[i].Vx=b[i].idy=b[i].Vy=0;
}
int l=1;
while(n<=m&&m>0){
while(a[l].V<K)++l;
++cnt;
b[cnt].idx=a[l].id;
b[cnt].Vx=K;
m--;
a[l].V-=K;
if(a[l].V==0){
swap(a[l],a[n]);
--n;
}
}
if(m>0){
if(m==n-2){
for(int i=1;i<=n;++i) v[i]=a[i].V-K;
f[0].reset();
f[0].set(M);
for(int i=1;i<=n;++i){
f[i]=f[i-1];
if(v[i]>=0) f[i]|=f[i-1]<<v[i];
else f[i]|=f[i-1]>>(-v[i]);
}
if(!f[n][M-K]){
puts("-1");
return;
}
q1.clear();q2.clear();
int now=M-K;
for(int i=n;i>=1;--i){
if(f[i-1][now-v[i]]){
q1.push_back(a[i]);
now=now-v[i];
}
else{
q2.push_back(a[i]);
}
}
sol(q1);sol(q2);
}
else if(m==n-1){
q1.clear();
for(int i=1;i<=n;++i) q1.push_back(a[i]);
sol(q1);
}
}
for(int i=1;i<=cnt;++i){
printf("%d %d",b[i].idx,b[i].Vx);
if(b[i].idy) printf(" %d %d",b[i].idy,b[i].Vy);
puts("");
}
return;
}
int main(){
int ttt=1;scanf("%d",&ttt);
while(ttt--) MAIN();
return 0;
}