题意:给定一个有向图,给一对点(s,t),可删除点的定义为:删除某个点之后使得s没有路径到达t,问有多少个可删除点。
思路:
想法1:
这题一开始我的思路就是用网络流来做。。就是先跑s->t的网络流,若最大流量==1则代表存在关键点,否则不存在关键点(>1和==0的情况分别讨论),然后找关键点就是每次找到可通过的边的最远的那个点开始第二次跑网络流。。之后。。发现不好处理。所以路就断了。。
之后我再想了其他方法来做。
想法2:
就是标记这些点 s–>x–> …->t 在图中把类似这种的x点标记,建立残图,这个时候残图一定是包含有关s->t的路径的。其他无关路径都没有加进来。再之后把这个有向图给弄成无向图。找割点。割点的个数+2就是答案了。。
后来发现太天真了。
碰到这种结构就完了。。看图。。
红色的点是割点。。但是红色的点并不是关键点。。我就丧心病狂的交了好多发最后还是无情的挂了。。
这种方法不行。。那就换思路吧。。
想法3:
突然想到。。先随意找一条s->t的路径。那么可删除点一定在这条路径上。因为除了这条路径的其他点一定不是可删除点。
那么问题就转换成就找这条路上的关键点。
也就是。假设s->a->b->c->d->e->t是我找的随意一条s->t的有向路径。
这个时候一个点若是可删除点的话那么一定满足这个条件:
从s出发不经过路径上其他点所能到达的最远的点。
看图:
假设我找的一条s->t的路径就是粉色点和黑色点。
这条路径上可删除点就是黑色的点。
每次找一个可删除点。每一次从可删除点为起点开始找下一个可删除点。
以此找下去。。
一开始代码是WA的。。然后观察了大神们的代码之后发现从后面遍历点才AC的。。
真是奇怪。。
for(int i=0;i<(int)e[now].size();i++){
和
for(int i=e[now].size()-1;i>=0;i–){
在bfs中找最短路有区别吗。。。
代码如下:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cstdlib> using namespace std; #define MAXN 100010 int pre[MAXN],vis[MAXN],low[MAXN]; vector <int> e[MAXN]; int bfs1(int st,int et){ queue <int> q; q.push(st); vis[st]=1;pre[st]=-1; while(!q.empty()){ int now=q.front(); q.pop(); for(int i=e[now].size()-1;i>=0;i--){ int next=e[now][i]; if(vis[next])continue; vis[next]=1; pre[next]=now; if(next==et)return 1; q.push(next); } } return 0; } int bfs2(int u){ queue <int> q; q.push(u); int res=u; vis[u]=1; while(!q.empty()){ int now=q.front(); q.pop(); for(int i=e[now].size()-1;i>=0;i--){ int next=e[now][i]; if(vis[next])continue; vis[next]=1; if(low[next]==0)q.push(next); else if(low[next]<low[res])res=next; } } return res; } int main() { int n,m,st,et; while(~scanf("%d%d",&n,&m)){ for(int i=0;i<=n;i++) e[i].clear(); memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low));
for(int i=0,x,y;i<m;i++){ scanf("%d%d",&x,&y); e[x].push_back(y); } scanf("%d%d",&st,&et); if(!bfs1(st,et)){ printf("%d\n",n); continue; }
int u=et,tot=1; memset(vis,0,sizeof(vis)); while(u!=-1){ low[u]=tot++; u=pre[u]; } int cnt=1; u=st; while(u!=et){ u=bfs2(u); ++cnt; } printf("%d\n",cnt); } return 0; }
|