20260715
今天是自由练习
P1948 [USACO08JAN] Telephone Lines S
思路
考虑二分答案,也就是路径中第 \(k - 1\) 长的电线。
每次判断答案时,建图。设当前判断的答案为 \(x\),则将边权大于 \(x\) 的边权值设为 \(1\),小于等于 \(x\) 的可以忽略,权值设为 \(0\) 即可。
接着对刚刚建出来的图跑一边 dijstra,返回 \(1\) 到 \(n\) 的最短距离即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 5;
int n, p, k, head[N], cnt, maxx, ans = -1, vis[N], dis[N];
struct edge {
int v, w, nxt;
} e[N * 2];
struct input_edge {
int a, b, l;
} a[N];
void addEdge(int x, int y, int z) {
e[++cnt].v = y;
e[cnt].w = z;
e[cnt].nxt = head[x];
head[x] = cnt;
}
int check(int x) {
cnt = 0;
for (int i = 1; i <= n; i++) {
head[i] = 0;
dis[i] = 1e9;
vis[i] = 0;
}
for (int i = 1; i <= p; i++) {
int weight = (a[i].l > x) ? 1 : 0;
addEdge(a[i].a, a[i].b, weight);
addEdge(a[i].b, a[i].a, weight);
}
deque<int> q;
q.push_back(1);
dis[1] = 0;
vis[1] = 1;
while (!q.empty()) {
int u = q.front();
q.pop_front();
vis[u] = 0;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
int w = e[i].w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) {
vis[v] = 1;
if (w == 0) q.push_front(v);
else q.push_back(v);
}
}
}
}
return dis[n];
}
signed main() {
cin >> n >> p >> k;
for (int i = 1; i <= p; i++) {
cin >> a[i].a >> a[i].b >> a[i].l;
maxx = max(maxx, a[i].l);
}
if (check(maxx + 1) == 1e9) {
cout << -1 << endl;
return 0;
}
int l = 0, r = maxx;
ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
int res = check(mid);
if (res <= k) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans << endl;
return 0;
}
P2343 宝石管理系统
这题数据好水。
思路
使用一个vector 来维护宝石。初始将所有宝石有序的加入vector。
插入操作就二分一下插入的位置,然后用 vector 自带的 insert 函数插入即可(但是这样插入好似是 \(O(n)\) 的,但是数据太水了还是过了。
查询操作直接输出 vector 中对应的数即可。
正解应该是平衡树之类的东西,但是我没写。
题解区还有不用二分纯暴力卡过的。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int m, q;
signed main()
{
cin >> m >> q;
vector<int> v;
for(int i = 1; i <= m; i ++)
{
int x; cin >> x;
v.insert(upper_bound(v.begin(), v.end(), x), x);
}
while(q --)
{
int c, n;
cin >> c >> n;
if(c == 1)
{
cout << v[v.size() - n] << endl;
}
else
{
v.insert(upper_bound(v.begin(), v.end(), n), n);
}
}
}
P1063 [NOIP 2006 提高组] 能量项链
水,但是我怎么没做出来。
暴力dp即可。
思路
由于项链是个环形的,考虑将环形拆成一个复制两遍的线形(长度为 \(2 \times n\))
设 \(dp[i][j]\) 为 \(a[i]\) 到 \(a[j]\) 中全部合并释放的最大能量。
我们再枚举一个 \(k\) 表示其中的断点,也就是 \(i\) 到 \(k\) 的与 \(k + 1\) 到 \(j\) 的合并。
则有状态转移方程 \(dp[j][i] = max(dp[j][i], dp[k + 1][i] + dp[j][k] + a[j] * a[k + 1] * a[i + 1])\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, a[205], ans, dp[205][205], dp2[205][205];
signed main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
a[i + n] = a[i];
}
for(int i = 0; i <= 2 * n; i ++) for(int j = 0; j <= 2 * n; j ++) dp[i][j] = 2e18;
for(int i = 2; i < 2 * n; i ++)
{
for(int j = i - 1; j > 0 && i - j < n; j --)
{
for(int k = j; k < i; k ++)
{
// cout << j << " " << i << endl;
dp[j][i] = max(dp[j][i], dp[k + 1][i] + dp[j][k] + a[j] * a[k + 1] * a[i + 1]);
}
ans = max(ans, dp[j][i]);
}
}
cout << ans << endl;
}
P1880 [NOI1995] 石子合并
和上一题差不多。
思路
和上一题一样。
维护区间石子数用前缀和就行。
状态转移方程和上一题差不多。
注意,只有区间长度恰好等于 \(n\) 时才能更新答案。求最小值的dp需要初始化为 \(inf\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, a[205], ans1, dp[205][205], dp2[205][205], sum[205], ans2 = 2e18;
signed main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
a[i + n] = a[i];
}
for(int i = 1; i <= 2 * n; i ++)
{
sum[i] = sum[i - 1] + a[i];
}
for(int i = 0; i <= 2 * n; i ++)
{
for(int j = 0; j <= 2 * n; j ++) dp2[i][j] = 2e18;
dp2[i][i] = 0;
}
for(int i = 2; i < 2 * n; i ++)
{
for(int j = i - 1; j > 0 && i - j < n; j --)
{
for(int k = j; k < i; k ++)
{
// cout << j << " " << i << endl;
dp[j][i] = max(dp[j][i], dp[k + 1][i] + dp[j][k] + sum[i] - sum[j - 1]);
dp2[j][i] = min(dp2[j][i], dp2[k + 1][i] + dp2[j][k] + sum[i] - sum[j - 1]);
}
ans1 = max(ans1, dp[j][i]);
if(i - j + 1 == n) ans2 = min(ans2, dp2[j][i]);
}
}
cout << ans2 << endl << ans1 << endl;
}
P3146 [USACO16OPEN] 248 G
依旧区间dp。
思路
设 \(dp[i][j]\) 为 \(i\) 到 \(j\) 的区间中全部合并后的最大值。
则枚举断点 \(k\),如果 \(dp[i][k] == dp[k + 1][j]\)(有两个相同的数),则转移 \(dp[j][i] = max(dp[j][i], dp[j][k] + 1)\)。
用 \(ans\) 数组记录 \(dp\) 中的最大值和初始给出的序列中的最大值(不然无法合并时便会得不到答案)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, a[250], dp[250][250], ans;
signed main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
dp[i][i] = a[i];
ans = max(ans, a[i]);
}
for(int i = 2; i <= n; i ++)
{
for(int j = i - 1; j > 0; j --)
{
for(int k = j; k < i; k ++)
{
if(dp[j][k] == dp[k + 1][i] && dp[j][k])
{
dp[j][i] = max(dp[j][i], dp[j][k] + 1);
// cout << dp[j][i] << " " << j << " " << i << endl;
ans = max(ans, dp[j][i]);
}
}
}
}
cout << ans << endl;
}

浙公网安备 33010602011771号