D. Bananas in a Microwave dp + 思维
题目大意:
一开始 (k = 0) ,然后你有两种操作:
- (Type1:t_i=1,x_i,y_i) ,选择一个 (a_i) ,满足 (0leq a_ileq y_i) ,然后对 (k) 进行下列操作 (a_i) 次,(k = left lceil (k+x_i) ight ceil)
- (Type2:t_i=2,x_i,y_i) 选择一个 (a_i) ,满足 (0leq a_ileq y_i) ,然后对 (k) 进行下列操作 (a_i) 次,(k = left lceil (k*x_i) ight ceil)
注意:(x_i) 是一个浮点数
问:经过最少多少次操作,最后 (k) 会变成 (j(1leq jleq m)) ,输出步数最少的次数,如果不能,那么输出 -1
题解:
这个题目还是满巧妙的,很好的一个思维题。
- 首先,很容易可以想到一个 (O(N*M^2)) 这个复杂度的方法。
- 但是,显然这个复杂度太高了,是因为每一个点的访问次数有 (N*M) 次
- 那么如果减少访问次数呢?
- 如果: (t,x,y=1,5,10) ,(k=10) ,那么接下来 (k = 15,20,...,55,60) ,假设 40之前以及被访问过,那么还需要访问40吗?
- 其实是不需要的,因为 (40) 被访问过,那么他的操作一定在这个之前,暂时还没有被访问,那么 (40) 之后一定会进行 (t,x,y=1,5,10) 这个操作,那么 (k = 40,45,...,90) ,所以连带着,上面40之后的所有数都不需要重新访问了。
- 这样的话,其实每一个数最多只会被访问 (N) 次
- 因为每次产生的新的数都会被保留下来,继续访问,如果访问到一个之前已经存下来的数,就不需要保留,因为之前往后转移时会覆盖这个结果。
最后感觉好像没有说的太明白,可以仔细看看 (CF) 的题解,非常好的一个题目,题解的代码也写的非常的漂亮。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int base = 1e5;
typedef long long ll;
typedef pair<int,int>pii;
queue<pii>que;
struct node{
ll x;
int t,y;
}e[maxn];
int ans[maxn];
bool is_seen[maxn];
inline ll Ceil(ll x,ll y){
return (x + y - 1) / y;
}
auto cal(int t,ll &val,ll x) {
if (t == 1) val += Ceil(x,base);
else val = Ceil(val*x,base);
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d%lld%d", &e[i].t, &e[i].x, &e[i].y);
memset(ans, -1, sizeof(ans));
ans[0] = 0;
is_seen[0] = true;
que.push(pii(0, 0));
while (!que.empty()) {
auto k = que.front().first;
auto time = que.front().second;
que.pop(), time++;
if (time > n) break;
auto t = e[time].t;
auto x = e[time].x;
auto y = e[time].y;
ll val = k;
for (int i = 1; i <= y; i++) {
cal(t, val, x);
if (val > m) break;
if (is_seen[val]) break;
is_seen[val] = true;
ans[val] = time;
que.push(pii(val, time));
}
que.push(pii(k, time));
}
for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
printf("
");
}