20260714
考试
T1 P3518 [POI 2011] SEJ-Strongbox
T2 P4095 [HEOI2013] Eden 的新背包问题
这一道题这么简单我怎么会做不出来。
考场思路
将所有情况全部预处理一遍,也就是二进制拆分后跑01背包。
然后询问时直接输出之前处理过的答案。
极限数据下大约要跑 \(3 \times 10 ^ {10}\),升级评测机后即可过。
80pts
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, dp[1005][1005], cnt, q;
int x, y, z, e, d, b;
struct node
{
int v, w, id;
}a[10005];
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
{
scanf("%d %d %d", &x, &y, &z);
b = 1;
for(; z - b >= 0; z -= b, b *= 2)
{
a[++ cnt] = {x * b, y * b, i};
}
if(z > 0) a[++ cnt] = {x * z, y * z, i};
}
for(int id = 1; id <= n; id ++)
{
for(int i = 1; i <= cnt; i ++)
{
if(a[i].id == id) continue;
for(int j = 1000; j >= a[i].v; j --)
{
dp[id][j] = (dp[id][j] > dp[id][j - a[i].v] + a[i].w) ? dp[id][j] : dp[id][j - a[i].v] + a[i].w;
}
}
}
scanf("%d", &q);
for(int i = 1; i <= q; i ++)
{
scanf("%d %d", &d, &e);
printf("%d\n", dp[d + 1][e]);
}
}
正解
分别从前往后和从后往前跑一边背包,存下来。
然后答案就是前面部分的 + 后面部分的了(刚好把询问的这个节点排除在外)。
但是东西都被二进制拆分掉了,但是拆除来的东西是连续的。
所以我们开一个 \(l\) 数组记录一个东西的左端点,再开一个 \(r\) 数组记录右端点。
发现前后分配的价值不是固定的,又发现价值特别的小,于是枚举价值计算最大值,得到答案
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, dp[1005][1005], cnt, q;
int x, y, z, e, d, b;
struct node
{
int v, w, id;
}a[10005];
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
{
scanf("%d %d %d", &x, &y, &z);
b = 1;
for(; z - b >= 0; z -= b, b *= 2)
{
a[++ cnt] = {x * b, y * b, i};
}
if(z > 0) a[++ cnt] = {x * z, y * z, i};
}
for(int id = 1; id <= n; id ++)
{
for(int i = 1; i <= cnt; i ++)
{
if(a[i].id == id) continue;
for(int j = 1000; j >= a[i].v; j --)
{
dp[id][j] = (dp[id][j] > dp[id][j - a[i].v] + a[i].w) ? dp[id][j] : dp[id][j - a[i].v] + a[i].w;
}
}
}
scanf("%d", &q);
for(int i = 1; i <= q; i ++)
{
scanf("%d %d", &d, &e);
printf("%d\n", dp[d + 1][e]);
}
}
T3 P4343 [SHOI2015] 自动刷题机
考场上其实写出了正解代码的,但是check函数的返回类型搞错了(一直没发现)
二分太久没写都不会写了
思路
一看到这个最小值最大值就想到二分。
观察答案,用大脑直觉思考一下,发现答案似乎存在单调性。
所以就可以用二分解决问题了。
记录一个 \(ans\) 数组来判断无解
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, m, a[N], dp[N][2], maxx;
int check(int x)
{
int now = 0, res = 0;
for(int i = 1; i <= n; i ++)
{
now += a[i];
if(now >= x)
{
res ++, now = 0; // 提交后代码清空,不是减去 x
}
if(now < 0) now = 0; // 删除超过已有代码时归零
}
return res;
}
signed main()
{
scanf("%lld %lld", &n, &m);
for(int i = 1; i <= n; i ++)
{
scanf("%lld", a + i);
}
int l = 1, r = 2e18;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid) <= m) r = mid - 1;
else l = mid + 1;
}
int ans_min = r + 1;
l = 1, r = 2e18;
while(l <= r)
{
int mid = l + r >> 1;
if(check(mid) >= m) l = mid + 1;
else r = mid - 1;
}
int ans_max = l - 1;
if(ans_min > ans_max || check(ans_min) != m) printf("-1");
else printf("%lld %lld", ans_min, ans_max);
return 0;
}

浙公网安备 33010602011771号