1 条题解
-
0
题意
有 种颜色的球,第 种颜色的球有 个。另外给定一个集合 ,里面有 个数 。
现在要把球堆成若干堆,要求:
- 每堆不能有重复颜色;
- 每堆的大小是 中的一个数。
请你最小化堆数,并输出方案。
题解
设最终有 堆,高度为 。交换堆的顺序肯定是没有影响的,为了方便,对于下文(包括后文移动完之后),我们不妨设 不增。
考虑如何判定这样一个序列合法。这也是我们得出答案序列后,求颜色方案的方法。

我们要求每一堆没有相同颜色,那么按照出现次数从大到小加入颜色。
对于每种颜色,从左往右,将离目标高度 最远的堆叠高一层。因为是优先叠那些最拖后腿的堆,这样的贪心是最优的。如果高度序列可能被达到,那么该最终状态合法。
因为我们是贪心地向上叠(先消耗出现次数多的颜色,先消耗比较高的堆,最可能合法),所以我们发现,对于一个合法局面,把前面堆的元素移到后面,也一定是合法的,如下图。

这是很好理解的,因为“拖后腿”的堆变矮了,颜色不重复的限制就变松了。
于是我们只需要限制最紧(找到前面的堆最多)的合法状态,再统计不超过这个的局面数量,就可以了。这个非常简单,只需要所有颜色都放一个前缀:

我们得到了尽可能最靠前放的序列,记其前缀和为 (如图),合法序列 一定满足:
- 单调不增排序后,
最小堆数看起来不是很好转移,那就设计一个可行性 DP:设 表示,考虑完了前 大的高度,用了 堆,目前前缀和为 ,是否可行。转移有两种,继承前一层 的值,和在本层中拿一个(相当于完全背包):
$$f_{i,j,k} \leftarrow f_{i-1,j,k} \vee f_{i,j-1,k-B_i} (k \leq Sum_j)$$括号内表示转移条件,另外我们已经将 降序排序,即 。
同时由于前缀和不超过 ,则 ,所以 是 级别的,再用
bitset优化可行性 DP(优化完全背包的部分),复杂度就降到了 ,就可以通过了。代码
看起来挺可读的。
#include<bits/stdc++.h> #define For(i,il,ir) for(int i=(il);i<=(ir);++i) #define Rof(i,ir,il) for(int i=(ir);i>=(il);--i) using namespace std; typedef pair<int,int> pii; const int maxn=15005; int n,m,V; int a[maxn],b[maxn],sum[maxn]; bitset<maxn> pw[maxn]; vector<bitset<maxn> > f[maxn]; void prework() { scanf("%d",&n); For(i,1,n){ scanf("%d",&a[i]); V+=a[i]; For(j,1,a[i]) sum[j]++; } pw[0].set(0); For(i,1,V){ sum[i]+=sum[i-1]; pw[i]=pw[i-1]<<1,pw[i].set(0); } scanf("%d",&m); For(i,1,m) scanf("%d",&b[i]); reverse(b+1,b+1+m); } int res[maxn]; vector<int> sol[maxn]; void dfs(int i,int j,int k){ if(!i||!j||!k) return; if(k>=b[i] && f[i][j-1][k-b[i]]) res[j]=b[i],dfs(i,j-1,k-b[i]); else dfs(i-1,j,k); } signed main() { prework(); bitset<maxn> tmp; tmp.set(0); f[0].push_back(tmp),b[0]=V+1; For(i,1,m) For(j,0,V/b[i]) { bitset<maxn> dp; if(j) dp|=(f[i][j-1]<<b[i]); if(b[i-1]*j<=V) dp|=f[i-1][j]; dp&=pw[sum[j]]; f[i].push_back(dp); } int ans=-1; For(j,0,V/b[m]) if(f[m][j][V]){ ans=j; break; } printf("%d\n",ans); if(ans==-1) return 0; else dfs(m,ans,V); priority_queue<pii> q,q2; For(i,1,n) q.push(make_pair(a[i],i)); For(i,1,ans) { printf("%d ",res[i]); while(res[i]--){ pii x=q.top(); q.pop(); printf("%d ",x.second); if(--x.first); q2.push(x); }printf("\n"); while(!q2.empty()) q.push(q2.top()),q2.pop(); } return 0; }
- 1
信息
- ID
- 7485
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者