这几天做了几道最小生成树的好题,分享一下。顺便写在这里提醒我:prim算法计算剩余最小边时要用一个数组记录维护,松弛时再更改,才能 O(n2) ,不然就是 O(n∗m) 。(最坏就是 O(n3) )
好题。你考虑如果现在我们定一个答案,那么我们人就可以变成一个园啦,如果走不动,那么拦住他的就是由那几个星星练成的一条路径,(想想一个胖子被门卡住走不动的样子,这个门就是星星或边界)。那么每个点再与上下边界连表,就变成了最小生成树。这是完全图,用Prim,然后边存不下,就可以先不存,扫的时候在算,然后更新后才加边。这样还不用打标记,造好了图直接跑就可以啦。(也可以用最短路,只不过松弛时改一下条件)
代码在另一篇博客中:传送门
就是我们要求路径上最短边权的最大值,那么就是一个最大生成树。建好图后在搞个Lca就好啦。顺便写一写倍增。
代码:
#include
#include
#include
using namespace std;
const int size = 10005;
const int inf = 2002093000;
struct EDGE{
int fr , to , next , val;
inline bool operator < (const EDGE &a) const
{ return val > a.val; }
}edge [10 * size] ; int cnt = 0;
int fa[size] , head[size] ;
int d[size] ;
int f[size][20] , g[size][20];
int n , m , q , num = 1 ;
void adde(int fr , int to , int val)
{
edge[++cnt] = (EDGE){fr , to , head[fr] , val };
head[fr] = cnt ;
}
void addedge(int fr , int to , int val)
{
adde(fr , to , val);
adde(to , fr , val);
}
int find(int x)
{
if (fa[x] == x) return x;
else return fa[x] = find( fa[x] );
}
bool unio(int x,int y)
{
x = find(x) ; y = find(y);
if (x != y)
{ fa[x] = y ; return true;}
else return false;
}
void dfs(int node , int fa )
{
d[node] = d[fa] + 1;
for (int i = head[node] ; i ; i = edge[i].next)
if ( edge[i].to != fa )
{
f[ edge[i].to ][ 0 ] = node; g[ edge[i].to ][ 0 ] = edge[i].val;
dfs(edge[i].to , node );
}
}
int lca(int x,int y)
{
int ans = inf;
if (d[x] < d[y]) swap(x , y);
int del = d[x] - d[y];
//printf("x is %d y is %d del is %d\n",x,y,del);
for (int i = 15; i >= 0;i--)
if (del & (1 << i) )
{
ans = min(ans , g[x][i]);
x = f[x][i];
}
if ( x == y ) return ans;
for (int i = 15 ; i >= 0 ; i--)
if ( f[x][i] != f[y][i] )
{
ans = min(ans , min( g[x][i] , g[y][i] ) );
x = f[x][i] ;
y = f[y][i] ;
}
return f[x][0] == 0 ? -1 : min(ans , g[x][0]);
}
int main()
{
scanf("%d%d" , &n , &m);
for (int i = 1 ; i <= m ; i++)
scanf("%d%d%d" , &edge[i].fr , &edge[i].to , &edge[i].val);
cnt = m ;
sort(edge + 1 , edge + 1 + cnt);
for (int i = 1 ; i <= n ; i++ ) fa[i] = i;
for (int i = 1 ; i <= m ; i++ )
{
if ( unio ( edge[i].to , edge[i].fr ) )
{ addedge( edge[i].fr , edge[i].to , edge[i].val ); }
}
memset(f,0,sizeof f);memset(g , 0 , sizeof g);
dfs ( 1 , 0 ) ;
for (int j = 1 ; j <= 15 ; j++)
for (int i = 1 ; i <= n ; i++)
{
//printf("%d %d %d %d\n ", i , j,f[i][j-1],g[i][j-1]);
f[i][j] = f[ f[i][j-1] ] [ j-1 ];
g[i][j] = min( g[i][ j-1 ] , g[ f[i][j-1] ][ j-1 ] );
}
/*for (int i = 1; i <= n;i++)
{
printf("%d is ",i);
for (int j = 0;j <= 3;j++)
printf("%d ",f[i][j]);
printf("\n");
}
/*for (int i = 1;i<=cnt;i++)
printf("%d is %d\n",i,edge[i].val);*/
scanf("%d" , &q);
for (int i = 1 ; i <= q ; i++)
{
int x,y;
scanf("%d%d" , &x , &y);
printf("%d\n",lca(x,y));
}
return 0;
}
这道题把我坑住的地方就是可以 O(n∗m) 算出每一个点,与它曼哈顿距离最近的敌人与他的距离,这个就是Bfs,类比网格图,所以这个是正确的。然后这个就好搞啦,实际上这道题与Starway差不多,然后由于距离一定是整数,就可以二分答案,当然,之前的Starway的做法也可以。(玄学的是,跑的最快的居然是二分。居然比SPFA和Prim还快)
代码(工程级别):
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 1005;
const int inf = 20010910;
struct CD{
int x , y;
}dot[size * 10];int n;
struct que{
int x , y , fa;
};
int x,y,stx,sty,edx,edy,step;
int d[size][size] ;
//bool vis[size][size] ;
int ans = 0;
int dist(int xx,int yy,int xxx,int yyy) { return abs(xx-xxx) + abs(yy-yyy); }
int dist(CD a,CD b) { return abs(a.x-b.x) + abs(a.y - b.y); }
int read()
{
int flag = 1 , x = 1; char s = getchar();
while (s < '0' || s > '9') {if (s=='-') flag = -1;s = getchar();}
while ('0'<=s && s<='9') {x = x*10 + s - '0' ; s = getchar();}
return flag * x;
}
void predo()// bfs 1
{
queue q;
for (int i = 0;i < x; i++)
for (int j = 0;j < y; j++)
d[i][j] = inf;
for (int i = 1;i <= n ; i++)
{
q.push( (que) {dot[i].x , dot[i].y , i} );
d[ dot[i].x ][ dot[i].y ] = 0;
}
while (!q.empty())
{
que cur = q.front();
if (cur.x < x - 1) if (d[ cur.x + 1 ][ cur.y] == inf)
{
d[ cur.x + 1 ][ cur.y ] = dist( dot[cur.fa].x , dot[cur.fa].y , cur.x + 1 , cur.y );
q.push( (que) {cur.x + 1 , cur.y , cur.fa});
}
if (cur.x > 0) if (d[ cur.x - 1 ][ cur.y] == inf)
{
d[ cur.x - 1 ][ cur.y ] = dist( dot[cur.fa].x , dot[cur.fa].y , cur.x - 1 , cur.y );
q.push( (que) {cur.x - 1 , cur.y , cur.fa});
}
if (cur.y < y - 1) if (d[ cur.x ][ cur.y + 1 ] == inf)
{
d[ cur.x ][ cur.y + 1 ] = dist( dot[cur.fa].x , dot[cur.fa].y , cur.x , cur.y + 1);
q.push( (que) {cur.x , cur.y + 1 , cur.fa});
}
if (cur.y > 0) if (d[ cur.x ][ cur.y - 1 ] == inf)
{
d[ cur.x ][ cur.y - 1 ] = dist( dot[cur.fa].x , dot[cur.fa].y , cur.x , cur.y - 1);
q.push( (que) {cur.x , cur.y - 1 , cur.fa});
}
q.pop();
}
}
void init()
{
n = read(); x = read(); y= read() , stx = read(), sty = read(), edx = read() , edy = read();
for (int i = 1;i <= n; i++)
dot[i].x = read() , dot[i].y = read();
}
bool check(int limit)
{
bool vis[size][size]; memset(vis , false , sizeof vis);
vis[stx][sty] = 1;
queue q; q.push( (CD) {stx , sty} );
while (!q.empty())
{
CD cur = q.front();
if (cur.x < x - 1) if (d[ cur.x + 1 ][ cur.y] >= limit && !vis[ cur.x + 1 ][ cur.y])
{
vis[ cur.x + 1 ][ cur.y ] = true ;
q.push( (CD) {cur.x + 1 , cur.y });
}
if (cur.x > 0) if (d[ cur.x - 1 ][ cur.y] >= limit && !vis[ cur.x - 1 ][ cur.y])
{
vis[ cur.x - 1 ][ cur.y ] = true ;
q.push( (CD) {cur.x - 1 , cur.y });
}
if (cur.y < y - 1) if (d[ cur.x ][ cur.y + 1 ] >= limit && !vis[ cur.x ][ cur.y + 1])
{
vis[ cur.x ][ cur.y + 1 ] = true;
q.push( (CD) {cur.x , cur.y + 1 });
}
if (cur.y > 0) if (d[ cur.x ][ cur.y - 1 ] >= limit && !vis[ cur.x ][ cur.y - 1])
{
vis[ cur.x ][ cur.y - 1 ] = true ;
q.push( (CD) {cur.x , cur.y - 1 });
}
q.pop();
}
if (!vis[edx][edy]) return false;
else return true;
}
void half()
{
int l = 0 , r = min(d[edx][edy] , d[stx][sty]);
while (l <= r)
{
int mid = l + r >> 1;
if (check(mid))
{
l = mid + 1;
ans= mid ;
}
else
r = mid - 1;
}
}
#define len fa
int find(int limit)
{
bool vis[size][size];memset(vis,false,sizeof vis);vis[stx][sty] = true;
queue q;q.push( (que) {stx , sty , 0} );
while (!q.empty())
{
que cur = q.front();
if (cur.x < x - 1) if (d[ cur.x + 1 ][ cur.y] >= limit && !vis[ cur.x + 1 ][ cur.y])
{
vis[ cur.x + 1 ][ cur.y ] = true ;
if (cur.x + 1 == edx && cur.y == edy) return (cur.len + 1);
q.push( (que) {cur.x + 1 , cur.y ,cur.len + 1});
}
if (cur.x > 0) if (d[ cur.x - 1 ][ cur.y] >= limit && !vis[ cur.x - 1 ][ cur.y])
{
vis[ cur.x - 1 ][ cur.y ] = true ;
if (cur.x - 1 == edx && cur.y == edy) return (cur.len + 1);
q.push( (que) {cur.x - 1 , cur.y ,cur.len + 1});
}
if (cur.y < y - 1) if (d[ cur.x ][ cur.y + 1 ] >= limit && !vis[ cur.x ][ cur.y + 1])
{
vis[ cur.x ][ cur.y + 1 ] = true;
if (cur.x == edx && cur.y + 1 == edy) return (cur.len + 1);
q.push( (que) {cur.x , cur.y + 1 ,cur.len + 1});
}
if (cur.y > 0) if (d[ cur.x ][ cur.y - 1 ] >= limit && !vis[ cur.x ][ cur.y - 1])
{
vis[ cur.x ][ cur.y - 1 ] = true ;
if (cur.x == edx && cur.y -1 == edy) return (cur.len + 1);
q.push( (que) {cur.x , cur.y - 1 ,cur.len + 1});
}
q.pop();
}
}
int main()
{
init();
predo();
half ();step = find(ans);
printf("%d %d" , ans , step);
return 0;
}
考思维的题果然好啊,我感觉学到了很多,尤其是套路。