Jzoj3177 安全监控
选举越来越近了,所以总统Amabo Kcarab准备在美国计划一次旅行,并在WDC和LA进行演讲。特务为了能够保护总统的安全,需要时刻监控所有总统会经过的城市(包括WDC和LA)。
当然,为了使预算不会太大,总统不会用到AF1,而会用汽车作为交通工具。并且,特务计划在总统从WDC到LA和回到WDC的旅途中安排尽可能少的需要监控的城市数目。
对于这个问题,我们假设美国有N(N<=100)个城市,标号为1到N,和M(M<=200)条单向的连接两个不同城市的州际公路。WDC的编号为1,LA的编号为2。
写一个程序计算出最少需要被监控的城市,使得有一条路只经过被监控的城市,可以从WDC到LA,最后回到WDC。
智商下降招来杀身之祸暂来机房躲避顺便更新一下
好的这道题目非常经典但是不是很好做
第一个做法是直接dfs,因为可以通过floyd求出上界来进行最优性剪枝,对于这个范围可以快速通过
但是这样不保险,我们还是考虑一下正经的做法
首先跑出dis[i][j]表示i到j的最短路长度,直接floyd
考虑用dp,设f[i][j]表示从j出发去到1,再从1到i所需的最少被监控的城市数量,显然f[1][1]=1,f[2][2]就是答案
运用类似dijkstra的做法,我们可以得到f的转移方程(用优先队列进行转移)
f[j][i]+dis[j][i]-1=>f[i][j] (i≠j)
f[i][j]+1=>f[v][j] ( i->v∈G且v≠j )
f[i][j]+1=>f[i][v] ( j->v∈~G且v≠i )
f[i][j]=>f[i][i] ( j->i∈~G )
f[i][j]=>f[j][j] ( i->j∈G )
其中G是原图,~G是反图
后面四个应该比较好理解,至于第一个式子,可以理解为:
f[j][i]表示i->1->j,那么再加上dis[i][j]就可以成为j->i->1->j->i,这个路径上除了j->i这一段是新增的其他应该和i->1->j相等,而j->i的点数即为dis[j][i]
让后直接跑dijkstra即可
#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110
using namespace std;
struct node{ int d,x,y; } t;
struct edge{ int v,nt; } G[N<<2];
int f[N][N],g[N][N],h[N],h2[N],n,m,cnt=0;
inline bool gmin(int& x,int y){ return x>y?x=y:0; }
inline bool operator< (node a,node b){ return a.d>b.d; }
inline void adj(int x,int y){
G[++cnt]=(edge){y,h[x]}; h[x]=cnt;
G[++cnt]=(edge){x,h2[y]}; h2[y]=cnt;
}
int main(){
scanf("%d%d",&n,&m);
memset(f,0x3f,sizeof f);
memset(g,0x3f,sizeof g);
for(int x,y;m--;){
scanf("%d%d",&x,&y);
f[x][y]=1; adj(x,y);
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
priority_queue<node> q;
g[1][1]=1; q.push((node){1,1,1});
for(int x,y;!q.empty();){
t=q.top(); q.pop();
if(g[x=t.x][y=t.y]<t.d) continue;
gmin(g[x][y],g[y][x]+f[y][x]-(x!=y));
for(int v,i=h[x];i;i=G[i].nt)
if(gmin(g[v=G[i].v][y],g[x][y]+(G[i].v!=y))) q.push((node){g[v][y],v,y});
for(int v,i=h2[y];i;i=G[i].nt)
if(gmin(g[x][v=G[i].v],g[x][y]+(G[i].v!=x))) q.push((node){g[x][v],x,v});
}
printf("%d",g[2][2]);
}