CF1941E. Rudolf and k Bridges
单调队列优化dp(滑动窗口)
题意:给定一个n*m的矩形,可以在任意一行建一座桥,建桥需要首先建支架,建立每个支架的代价为
a
i
,
j
+
1
a_{i,j}+1
ai,j+1,对于支架的要求是,在桥的两端必须要建支架,即
(
i
,
1
)
,
(
i
,
m
)
(i,1),(i,m)
(i,1),(i,m)(i表示第i行),其余的支架间隔不能超过d,然后要连续建k座桥,问最小的建桥代价?
思路:这道题很明显的用单调队列优化dp,跟滑动窗口一样,用单调队列优化dp求出每一行建桥的代价后,再对列求一遍前缀和,找出最小子段和即可。具体看代码即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 2e18;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
int n,m,k,d;cin>>n>>m>>k>>d;
vector<vector<ll>>a(n,vector<ll>(m));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j];
vector<ll>s(n+1);
for(int i=0;i<n;i++)
{
deque<int>q;q.push_back(0);
vector<ll>dp(m+1,INF);
dp[0]=1;//记得初始化边界
for(int j=1;j<m;j++)//滑动窗口
{
while(!q.empty()&&j-q.front()-1>d)q.pop_front();
dp[j]=dp[q.front()]+a[i][j]+1;
while(!q.empty()&&dp[j]<=dp[q.back()])q.pop_back();
q.push_back(j);
}
s[i+1]=s[i]+dp[m-1];//求前缀和
}
ll minv=INF;
for(int i=k;i<=n;i++)minv=min(minv,s[i]-s[i-k]);//最大子段和
cout<<minv<<"\n";
}
return 0;
}
CF1941F. Rudolf and Imbalance
思维+二分
题意:给定一个升序排列的数组a,再给两个数组b和c,要求插入
b
j
+
c
k
b_j+c_k
bj+ck到数组a中,使得数组a的
a
i
−
a
i
−
1
(
i
>
1
)
a_i-a_{i-1}(i>1)
ai−ai−1(i>1)的最大值最小
思路:首先我们求出最大的
a
i
−
a
i
−
1
a_i-a_{i-1}
ai−ai−1的值
m
a
x
v
1
maxv1
maxv1,再求出次大值
m
a
x
v
2
maxv2
maxv2,那么我们肯定是插入一个值后使得
m
a
x
v
1
maxv1
maxv1变小,变小之后再跟
m
a
x
v
2
maxv2
maxv2比较,得到最小的最大值,很明显,要想使
a
i
−
a
i
−
1
a_i-a_{i-1}
ai−ai−1的值变小,我们最好的情况就是在
a
i
a_i
ai和
a
i
−
1
a_{i-1}
ai−1的中间值,即
(
a
i
+
a
i
−
1
)
/
2
(a_i+a_{i-1})/2
(ai+ai−1)/2,记为need,因此我们可以遍历数组b,用
n
e
e
d
−
b
j
need-b_j
need−bj后二分查找数组c中
n
e
e
d
−
b
j
need-b_j
need−bj左右两边最近的值,之所以要比较
n
e
e
d
−
b
j
need-b_j
need−bj左右两边最近的值,是因为我们不一定恰好就能找到一个
c
k
+
d
j
=
=
n
e
e
d
c_k+d_j==need
ck+dj==need,找到的
c
k
c_k
ck可能比
n
e
e
d
need
need大也有可能比
n
e
e
d
need
need小,之后将找到的这个值
c
k
+
b
j
c_k+b_j
ck+bj插入到
a
i
a_i
ai和
a
i
−
1
a_{i-1}
ai−1的中间,此时再求数组中的不平衡值,与
m
a
x
v
2
maxv2
maxv2比较
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 2e18;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
int n,m,k;cin>>n>>m>>k;
vector<ll>a(n),d(m),f(k);
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<m;i++)cin>>d[i];
for(int i=0;i<k;i++)cin>>f[i];
sort(d.begin(),d.end());
sort(f.begin(),f.end());
ll mx1=0,mx2=0;//求出最大值与次大值
for(int i=1;i<n;i++)
{
ll num=a[i]-a[i-1];
if(num>mx1)mx2=mx1,mx1=num;
else if(num>mx2)mx2=num;
}
ll ans=mx1;
for(int i=1;i<n;i++)
{
if(a[i]-a[i-1]==mx1)
{
ll need = (a[i-1]+a[i])/2;//理想的插入值
for(int j=0;j<m;j++)//遍历数组d。在数组f中找到最优的值
{
auto it = lower_bound(f.begin(),f.end(),need-d[j]);
//判断左右两边的值与mx2的大小情况
if(it!=f.end())
{
ll num = *it + d[j];
if(num<=a[i])
ans=min(ans,max({mx2,a[i]-num,num-a[i-1]}));
}
if(it!=f.begin())
{
ll num = *prev(it) + d[j];
if(num>=a[i-1])
ans=min(ans,max({mx2,a[i]-num,num-a[i-1]}));
}
}
break;
}
}
cout<<ans<<"\n";
}
return 0;
}
CF1941G. Rudolf and Subway
01bfs求最短路
题意:给定一张无自环的图,这张图表示一张地铁路线图,图中的边有颜色,每个颜色视为一条地铁线,在颜色相同的边连接的点之间行走是不需要费用的,否则需要一元的费用,给定起点和终点要求从起点到终点的最小费用花费。
思路:我们可以为每个颜色建立一个虚拟节点,对于这个颜色连接的所有点,都连一条指向虚拟节点边权为1的有向边,然后从这个虚拟节点向这个颜色的所有点都连一条边权为0的所有边,这样就可以表示在一条地铁线上走不需要花费费用,因为边权只有0和1,我们就可以用01bfs,即双端队列bfs求最短路,就可以得到从起点到终点的最短路了
代码如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <deque>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 10, INF = 1e9+10;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
int n,m;cin>>n>>m;
map<int,int>mp;
vector<vector<pii>>g(n+m+1);//最多m个颜色,每个颜色编一个号,总过最多n+m+1个点
while(m--)
{
int u,v,w;cin>>u>>v>>w;
if(!mp.count(w))mp[w]=++n;//mp[w]为该颜色对应的虚拟节点的编号
g[u].push_back({mp[w], 1});//有颜色的点向虚拟节点连一条边权为1的点
g[mp[w]].push_back({v, 0});//虚拟节点向有颜色的点连一条边权为0的点
g[v].push_back({mp[w], 1});//另一个点同样的操作
g[mp[w]].push_back({u, 0});
}
int st,fin;cin>>st>>fin;
deque<int>q;
vector<int>d(n+1,INF),vis(n+1,0);//双端队列跑一边01bfs求最短路
d[st]=0;q.push_back(st);
while(q.size())
{
int u=q.front();q.pop_front();
if(vis[u])continue;
vis[u]=1;
for(auto [v, w] : g[u])
{
if(d[v] > d[u] + w)
{
d[v] = d[u] + w;
if(w)q.push_back(v);
else q.push_front(v);
}
}
}
cout<<d[fin]<<"\n";
}
return 0;
}
···