赛前训练1 最短路
T1
分层图一般套路:dis 数组多设置一维表示层数、松弛时讨论是否换层即可。
参考实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5,K=15;
int n,m,k,s,t;
struct EDGE{
int v,w;
};
struct NODE{
int v,w,l;
bool operator < (const NODE &b) const{
return w>b.w;
}
};
int dis[N][K];
bool vis[N][K];
vector<EDGE> G[N];
void dijkstra(){
memset(dis,0x3f,sizeof dis);
dis[s][0]=0;
priority_queue<NODE> pq;
pq.push({s,0,0});
while(!pq.empty()){
auto cur=pq.top();
pq.pop();
if(vis[cur.v][cur.l])
continue;
vis[cur.v][cur.l]=1;
for(auto i:G[cur.v]){
if(dis[i.v][cur.l]>dis[cur.v][cur.l]+i.w){
dis[i.v][cur.l]=dis[cur.v][cur.l]+i.w;
pq.push({i.v,dis[i.v][cur.l],cur.l});
}
if(cur.l<k&&dis[i.v][cur.l+1]>dis[cur.v][cur.l]){
dis[i.v][cur.l+1]=dis[cur.v][cur.l];
pq.push({i.v,dis[i.v][cur.l+1],cur.l+1});
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k>>s>>t;
s++,t++;
for(int i=1,u,v,w;i<=m;i++){
cin>>u>>v>>w,u++,v++;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
dijkstra();
int ans=1e9;
for(int i=0;i<=k;i++)
ans=min(ans,dis[t][i]);
cout<<ans;
return 0;
}
T2
奇偶最短路:用于判断 \(a \to b\) 是否能走 \(x\) 步到达。
具体而言,维护 dis1 表示奇数最短路,dis2 表示偶数最短路。
dijkstra 中转移时,根据奇偶变化,dis2 松弛 dis1,dis1 松弛 dis2 即可。
判断时,若 \(x\) 为奇数且大于等于 \(dis1_{a,b}\),或 \(x\) 为偶数且大于等于 \(dis2_{a,b}\),都是能走到的,否则不能走到。
对于这个题,需要判断孤点的情况,因为孤点也跑 bfs 的话会导致其 \(dis2=0\),从而误认为其他点能从它开始走到。
参考实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e3+5,K=1e6+5;
int n,m,k;
struct NODE{
int t,d,id;
};
vector<NODE> q[K];
vector<int> G[N];
int dis[N][2];
bool vis[N],ans[K];
void bfs(int s){
memset(dis,0x3f,sizeof dis);
queue<int> q;
q.push(s);
vis[s]=1;
dis[s][0]=0;
while(!q.empty()){
int cur=q.front();
vis[cur]=0;
q.pop();
for(int i:G[cur]){
if(dis[i][0]>dis[cur][1]+1){
dis[i][0]=dis[cur][1]+1;
if(!vis[i])
vis[i]=1,q.push(i);
}
if(dis[i][1]>dis[cur][0]+1){
dis[i][1]=dis[cur][0]+1;
if(!vis[i])
vis[i]=1,q.push(i);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1,s,t,d;i<=k;i++){
cin>>s>>t>>d;
q[s].push_back({t,d,i});
}
for(int i=1;i<=n;i++){
if(q[i].size()&&G[i].size()){
bfs(i);
for(auto j:q[i])
if(j.d%2==0&&j.d>=dis[j.t][0]||j.d%2==1&&j.d>=dis[j.t][1])
ans[j.id]=1;
}
}
for(int i=1;i<=k;i++)
cout<<(ans[i]?"TAK\n":"NIE\n");
return 0;
}
T3
最短路分割 + 贪心
对于 \(u,v\),有 \(dis_{1,n}=dis_{1,u}+dis_{u,v}+dis_{v,n}\),现在 \(dis_{u,v}\) 将被替换为 \(1\),我们希望变化尽可能小,显然 \(dis_{u,v}\) 应当尽可能小。
从贪心的角度考虑,\(dis_{u,v}\) 尽可能小,等价于 \(u,v\) 和 \(1\) 的距离之差尽可能小,于是我们可以讲 \(S\) 按照与 \(1\) 的距离排序,每次考虑选择相邻节点连边即可。
值得注意的是,我连了边,最短路可能并不经过它,所以我们最后还得和原图最短路取 \(\min\)。
参考实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,m,k;
int a[N],dis1[N],dis2[N];
bool vis[N];
vector<int> G[N];
bool cmp(int x,int y){
return dis1[x]<dis1[y];
}
void bfs(int s,int *dis){
for(int i=1;i<=n;i++)
dis[i]=1e18;
memset(vis,0,sizeof vis);
queue<int> q;
q.push(s);
dis[s]=0;
while(!q.empty()){
int cur=q.front();
q.pop();
if(vis[cur])
continue;
vis[cur]=1;
for(int i:G[cur]){
if(dis[i]>dis[cur]+1){
dis[i]=dis[cur]+1;
q.push(i);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
cin>>a[i];
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
bfs(1,dis1),bfs(n,dis2);
sort(a+1,a+k+1,cmp);
int ans=-1e18;
for(int i=1;i<k;i++)
ans=max(ans,dis1[a[i]]+dis2[a[i+1]]+1);
cout<<min(ans,dis1[n]);
return 0;
}
T4
差分约束。
简单来说,对于诸如下面这样的不等式组:
将 \(x_i\) 向 \(y_i\) 建一条权值为 \(w_i\) 的有向边(\(i \in [1,m]\)),并虚拟 \(0\) 号源点并向所有 \(x_i\) 连一条权值为 \(0\) 的有向边,即可得到一张有向图。
可以通过在这张有向图判断能不能跑出最短路(即判负环)的方式证明不等式组有无解,画图易证。
把不等式组的所有符号换成 \(\le\),则通过判断能不能跑出最长路(即判正环)亦可解决。
同时,最长路通常解决最小化问题,最短路通常解决最大化问题。
回归本题,看到「至少」等字眼,我们需要发现差分约束模型。
运用前缀和的思想,我们令 \(sum_i\) 表示前 \(i\) 小时的收银员数量。
显然,\(sum_i-sum_{i-1} \ge 0\)。
同时,\(sum_i-sum_{i-1} \le t_i\),其中 \(t_i\) 表示第 \(i\) 小时申请的人数。
考虑到还有个下界 \(R_i\),因此有 \(sum_i-sum_{i-8} \ge R_i\)。
依照上述不等式建图,然后枚举最少人数并跑正环判断即可。需要特判 \(i-8<0\) 的情况。
参考实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=48;
int _,n;
int r[N],t[N],dis[N],cnt[N];
bool vis[N];
struct EDGE{
int v,w;
};
vector<EDGE> G[N];
bool spfa(int s){
memset(dis,-0x3f,sizeof dis);
dis[s]=0;
memset(vis,0,sizeof vis);
memset(cnt,0,sizeof cnt);
vis[s]=1;
queue<int> q;
q.push(s);
while(!q.empty()){
int cur=q.front();
q.pop();
vis[cur]=0;
for(auto i:G[cur]){
if(dis[i.v]<dis[cur]+i.w){
dis[i.v]=dis[cur]+i.w;
cnt[i.v]=cnt[cur]+1;
if(cnt[i.v]>24)
return 0;
if(!vis[i.v])
vis[i.v]=1,q.push(i.v);
}
}
}
return 1;
}
bool check(int x){
for(int i=0;i<=24;i++)
G[i].clear();
G[0].push_back({24,x});
for(int i=1;i<=24;i++){
G[0].push_back({i,0});
G[i-1].push_back({i,0});
G[i].push_back({i-1,-t[i]});
if(i<=8)
G[i+16].push_back({i,r[i]-x});
else
G[i-8].push_back({i,r[i]});
}
return spfa(0);
}
void solve(){
memset(t,0,sizeof t);
for(int i=1;i<=24;i++)
cin>>r[i];
cin>>n;
for(int i=1,x;i<=n;i++)
cin>>x,x++,t[x]++;
for(int i=1;i<=n;i++){
if(check(i)){
cout<<i<<'\n';
return;
}
}
cout<<"No Solution\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>_;
while(_--)
solve();
return 0;
}

浙公网安备 33010602011771号