USACO 3.1 丑数
题目:https://www.luogu.org/problemnew/show/P2723
强烈谴责quq:这题根本不是黄题!!
根据题意,若令丑数集合为f,目前在第i个丑数,我们要判断f[i]的值
由于现在的丑数都是由另一个丑数乘以个数得来(我们假设f[0] = 1),利用类似dp的思想,我们知道f[i] = min(f[t] * a[j]),其中f[t]表示之前的丑数,j for 1 - k; 找到最小的值即可
直接这么做是O(n^2k)的,凉的很彻底orz
考虑优化:由于f是递增的,因此对于每一个j,枚举到第一个满足条件的t即可跳出
这么做理论复杂度依然没变,继续考虑优化
假设我们已经计算完f[i - 1],它可以由f[t] * a[j]得来,那么在计算f[i]时,枚举f只能从t开始,因为<t的f的值都不符合f[i - 1]的条件,自然不可能符合f[i]
那么用一个b数组记录j的上一个的t,在枚举时从b[j]开始即可
这样的复杂度变为O(nk + n),可以AC
以上
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<climits> using namespace std; const int maxn = 1e5 + 1; inline int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (ans *= 10) += ch - '0'; ch = getchar(); } return ans * op; } int k,n; int a[105],f[maxn]; int b[105]; //f[t] * a[j] > f[i - 1] int main() { k = read(),n = read(); for(int i = 1;i <= k;i++) a[i] = read(); f[0] = 1; for(int i = 1;i <= n;i++) { int minm = INT_MAX; for(int j = 1;j <= k;j++) for(int t = b[j];t < i;t++) if(f[t] * a[j] > f[i - 1]) { minm = min(minm,f[t] * a[j]); b[j] = t; break; } f[i] = minm; } printf("%d",f[n]); }