D. Toss a Coin to Your Graph
题意:给定一个有向图,每次可以从任意节点出发找一条长度为k-1的路径,要求,求出路径上点权最大值最小是多少。
思路:首先我们不难想到这道题的基本思路是二分答案+DFS,根据图的特性重点放在设计DFS操作上
提示如下:
1.有向图,可以有环,所以当我们在DFS的过程中遇到一个合法环就可以直接判为合法路径(一下子用完k步),所以需要判环操作
2.普通在无环情况下我们要找的无疑是一条链,所以还需要统计从某个节点出发的最长链
3.在选择节点走时必要条件是选择的这个节点的权值不能大于我们想要的二分值
DFS之后判断是否存在合法路径,也就是是否存在以某个点为起点的合法路径长度要>=k
ACcode
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
struct node
{
int nex,to;
};
node edge[N];
int head[N],tot;
bool vis[N];//判访问
int w[N];//权值
int n,m,k;
int dp[N];//从i开始的最长合法路径
bool st[N];//判环
void add(int from,int to)
{
edge[++tot].nex=head[from];
edge[tot].to=to;
head[from]=tot;
}
void dfs(int now,int x)
{
st[now]=true;
vis[now]=true;
dp[now]=1;
for(int i=head[now];i;i=edge[i].nex)
{
int to=edge[i].to;
if(w[to]<=x)
{
if(st[to])//又访问已经经过的节点,说明形成了环
{
dp[now]=k;
break;
}
else
{
if(!vis[to])//没有走过的节点继续选择
{
dfs(to,x);
}
dp[now]=max(dp[now],dp[to]+1);
}
}
}
st[now]=false;//
}
bool check(int x)
{
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
if(!vis[i]&&w[i]<=x)
dfs(i,x);
for(int i=1;i<=n;i++)
if(vis[i]&&dp[i]>=k)
return true;
return false;
}
signed main()
{
/*cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);*/
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&w[i]);
}
while(m--)
{
int a,b;
scanf("%lld%lld",&a,&b);
add(a,b);
}
int l=1,r=1e9+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
}
else
{
l=mid+1;
}
}
if(r==1e9+1)
{
cout<<-1<<'\n';
}
else
{
cout<<l<<'\n';
}
return 0;
}
关于st数组的判环操作
我们在真正形成一个环的时候显然用不到外层的st[now]=false操作
但是在不能形成一个环的时候,假设一个点的入度为2出度为0,设想没有st[now]=false操作的话会怎样。