Luogu P1983 车站分级 题解 [ 绿 ] [ 拓扑排序 ] [ 图论建模 ] [ 虚点 ]
车站分级:很好的拓扑排序题,细节有点多。
图论建模
首先观察对于一条线路,我们可以从中直接得到什么信息:
假设这条线路的开头为
对于这一点条件,我们就可以建模了。
和差分约束一样,我们从某个点起连到所有比其多
然后拓扑排序跑最长路即可。不是最短路的原因是这些都是充分必要条件,每一个都必须满足,因此要选最长路。
这样建图是
优化
发现复杂度瓶颈在于是一堆点往一堆点连边,所以我们从优化建边的方式入手:虚点优化。
我们把那些原来的起点们连多条向这个虚点的有向边,边权赋为
总体复杂度是
细节
边权全部赋值为 的做法
对于这种做法,非常万能,但我想不到。
写这种做法细节很少,我们无需处理起点是
最后统计答案的时候,我们只需要将
同时注意有可能虚点入度为
in:
3 1
2 1 2
out:
1
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1005],dp[3005],rd[3005],ans=0;
vector<int>g[3005];
queue<int>q;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int s,st=0,ed=0;
cin>>s;
bitset<1005>isin;
for(int j=1;j<=s;j++)
{
cin>>a[j];
isin[a[j]]=1;
if(j==1)st=a[j];
if(j==s)ed=a[j];
}
int vt=n+i;
for(int j=st;j<=ed;j++)
{
if(isin[j]==0)
{
g[j].push_back(vt);
rd[vt]++;
}
else
{
g[vt].push_back(j);
rd[j]++;
}
}
}
for(int i=1;i<=n+m;i++)
{
if(rd[i]==0)
{
q.push(i);
dp[i]=0;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto v:g[u])
{
dp[v]=max(dp[v],dp[u]+1);
rd[v]--;
if(rd[v]==0)
{
q.push(v);
}
}
}
for(int i=1;i<=n+m;i++)
{
ans=max(ans,dp[i]/2+1);
}
cout<<ans;
return 0;
}
对于边权先赋为 ,后面再赋为 的做法
依然是上面的那个 hack 数据:
in:
3 1
2 1 2
out:
1
如果把入度为
另外,不要读错题了,只有起点和终点之间的车站才受分级的约束,而不是全部车站。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[1005],dp[3005],rd[3005],ans=0;
struct edge{
int to,w;
};
vector<edge>g[3005];
queue<int>q;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int s,st=0,ed=0;
cin>>s;
bitset<1005>isin;
for(int j=1;j<=s;j++)
{
cin>>a[j];
isin[a[j]]=1;
if(j==1)st=a[j];
if(j==s)ed=a[j];
}
int vt=n+i;
for(int j=st;j<=ed;j++)
{
if(isin[j]==0)
{
g[j].push_back({vt,0});
rd[vt]++;
}
else
{
g[vt].push_back({j,1});
rd[j]++;
}
}
}
for(int i=1;i<=n;i++)
{
if(rd[i]==0)
{
q.push(i);
dp[i]=1;
}
}
for(int i=n+1;i<=n+m;i++)
{
if(rd[i]==0)
{
q.push(i);
dp[i]=0;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto tmp:g[u])
{
int v=tmp.to,w=tmp.w;
dp[v]=max(dp[v],dp[u]+w);
rd[v]--;
if(rd[v]==0)
{
q.push(v);
}
}
}
for(int i=1;i<=n+m;i++)
{
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战