解题报告-洛谷P3462 [POI 2007] ODW-Weights
P3462 [POI 2007] ODW-Weights
题目描述
在搬迁到一个新的园区时,Byteotian 实验物理研究所遇到了一个后勤问题——转移其庞大的精密砝码收藏变得不那么简单。
研究所有若干个强度有限的容器可供使用。需要尽可能多地将砝码放入容器中,剩下的将被丢弃。除了不超过容器的强度外,放入容器中的砝码数量没有限制。一个容器也可以是空的。
研究所的任意两个砝码有一个特殊的性质:其中一个的质量是另一个质量的整数倍。特别地,它们可能具有相同的质量。
任务编写一个程序:
从标准输入中读取容器的强度和砝码的质量,确定可以放入容器中的最大砝码数量,将结果写入标准输出。
输入格式
标准输入的第一行包含两个整数 \(n\) 和 \(m\)(\(1\le n,m\le 100\ 000\)),用单个空格分隔,分别表示容器的数量和砝码的数量。标准输入的第二行由 \(n\) 个整数 \(w_i\)(\(1\le w_i\le 1\ 000\ 000\ 000\),\(1\le i\le n\))组成,用单个空格分隔,表示容器的强度(单位:毫克)。第三行有 \(m\) 个整数 \(c_j\)(\(1\le c_j\le 1\ 000\ 000\ 000\),\(1\le j\le m\)),用单个空格分隔,表示砝码的质量(单位:毫克)。
输出格式
标准输出的第一行应包含一个整数——可以放入容器中的最大砝码数量,而不超过其强度。
输入输出样例 #1
输入 #1
2 4
13 9
4 12 2 4
输出 #1
3
说明/提示
(由 ChatGPT 4o 翻译)
解题报告
算法:贪心、进制
开了眼了,没想到可以用进制来贪心。
首先,显然将原数组 $ { c_j } $ 排序后,对于 $ \forall j \in [2,m] \(,\) \exist k \in \N_+ $,使得 $ k \times c_{j-1}=c_j $。
然后有一个经典的贪心操作:对于每个 \(c_j\),尽可能选择一个当前最小的可以装下 \(c_j\) 的 \(w_i\),将 \(w_i\) 修改为 \(w_i-c_j\)。
然后有一个很妙的解法。
我们设数组 \(\{ p_k \}\) 为数组 \(\{ c_j \}\) 去重后从大到小排序后的结果。
那么就可以把每个 \(w_i\) 拆为 \(\sum ( t_k \times p_k)\),同时为了拆分的唯一性和便于贪心,我们让更大的 \(p_k\) 对应的系数 \(t_k\) 尽可能的大。
这里就可以看出来了,我们在进行一个类似进制转化的过程,系数数组 \(\{t_k\}\) 就是转化的结果,数组 \(\{p_k\}\) 就是进制规则(逢 \(p_k\) 进 \(1\))。
那么,我们只需把每个 \(w_i\) 的系数 \(\{ t_k \}\) 按 \(p_k\) 统计为 \(cnt_{p_k}\),对每一个 \(c_j\),判断 \(cnt_{c_j}\) 是否为 \(0\),是的话尝试向更高位借 \(1\),借不到则无法装下了,否则可以成功装下。这和之前的贪心思路等价。
于是解决了,时间复杂度 \(O( n \log max\{ w_i \} )\)。
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=101100;
const int lg=32;
#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )
inline int read()
{
int f=1,x=0; char ch=getchar();
while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar(); }
return f*x;
}
int n,m,ans,w[N],c[N];
int cnt[lg],p[lg],pos[N],tot;
bool dfs(int i)
{
if(i>tot) return false;
if(cnt[i]) { cnt[i]--;return true; }
if(dfs(i+1))
{
cnt[i]+=p[i+1]/p[i]-1;
return true;
}
return false;
}
signed main()
{
freopen("mushroom.in","r",stdin);
freopen("mushroom.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<=m;i++) c[i]=read();
sort(c+1,c+m+1);
for(int i=1;i<=m;i++)
{
if(c[i]!=c[i-1])
p[++tot]=c[i];
pos[i]=tot;
}
for(int i=1;i<=n;i++)
for(int j=tot;j;j--)
{
if(!w[i]) break;
cnt[j]+=w[i]/p[j];
w[i]%=p[j];
}
for(int i=1;i<=m;i++)
if(dfs(pos[i])) ans++;
else break;
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号