P9339 题解
以下设
首先发现盒子和饼干之间是类似匹配的关系,因此我们以盒子为左部点,每种饼干为右部点,连边即为完全二分图,代表每种饼干只能装到每个盒子中一个。题目所求即为最少的左部点数量使得该图有完美匹配,输出方案。
完美匹配考虑霍尔定理,首先要
我们注意到等式右边的取值只与集合大小有关,可以对
直接使用背包,先对
但是我们注意到在
至于输出方案,先在 DP 数组上倒着把
#include<iostream>
#include<algorithm>
#include<bitset>
#include<queue>
#define bs bitset <N>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=15000+10;
int n,m,s,r=-1,a[N],b[N],p[N],c[N],w[N];
bool cmpa(int x,int y) {return a[x]<a[y];}
bool cmp(int x,int y) {return x>y;}
bs lm[N];
vector <bs> f[N];
vector <pii> tv;
priority_queue <pii> q;
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],s+=a[i],p[i]=i;
cin>>m;
for(int i=1;i<=m;i++) cin>>b[i];
sort(p+1,p+1+n,cmpa),sort(b+1,b+1+m,cmp);
int tp=0,ts=0;
for(int i=1;i<=s;i++)
{
while(tp<n&&a[p[tp+1]]<=i) tp++,ts+=a[p[tp]];
c[i]=ts+(n-tp)*i;
}
b[0]=s+1,lm[0][0]=1;
for(int i=1;i<=s;i++) lm[i]=lm[i-1],lm[i][i]=1;
for(int i=0;i<=m;i++) f[i].resize(s/b[i]+1);
f[0][0][0]=1;
for(int i=1;i<=m;i++) for(int j=0;j<=s/b[i];j++)
{
if(j<=s/b[i-1]) f[i][j]=f[i-1][j];
if(j) f[i][j]|=(f[i][j-1]<<b[i]);
f[i][j]&=lm[c[j]];
}
for(int i=1;i<=s/b[m];i++) if(f[m][i][s]) {r=i; break;}
cout<<r<<'\n';
if(r==-1) return 0;
int cur=s,tw=m;
for(int i=1;i<=r;i++) for(int j=tw;j>=1;j--)
if(f[j][r-i+1][cur]&&f[j][r-i][cur-b[j]])
{w[i]=b[j],cur-=b[j],tw=j; break;}
for(int i=1;i<=n;i++) q.push({a[i],i});
for(int i=1;i<=r;i++)
{
cout<<w[i]<<' ',tv.clear();
for(int j=0;j<w[i];j++)
{
pii te=q.top(); q.pop();
te.fi--,cout<<te.se<<' ';
if(te.fi) tv.push_back(te);
}
for(pii te:tv) q.push(te);
cout<<'\n';
}
return 0;
}