最小生成树的思维好题

前言:

这几天做了几道最小生成树的好题,分享一下。顺便写在这里提醒我:prim算法计算剩余最小边时要用一个数组记录维护,松弛时再更改,才能 On2 ,不然就是 Onm 。(最坏就是 On3

T1 Starway:

好题。你考虑如果现在我们定一个答案,那么我们人就可以变成一个园啦,如果走不动,那么拦住他的就是由那几个星星练成的一条路径,(想想一个胖子被门卡住走不动的样子,这个门就是星星或边界)。那么每个点再与上下边界连表,就变成了最小生成树。这是完全图,用Prim,然后边存不下,就可以先不存,扫的时候在算,然后更新后才加边。这样还不用打标记,造好了图直接跑就可以啦。(也可以用最短路,只不过松弛时改一下条件)
代码在另一篇博客中:传送门

T2 货车运输:

就是我们要求路径上最短边权的最大值,那么就是一个最大生成树。建好图后在搞个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;
}

T3 大逃亡:

这道题把我坑住的地方就是可以 Onm 算出每一个点,与它曼哈顿距离最近的敌人与他的距离,这个就是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;
}

结语

考思维的题果然好啊,我感觉学到了很多,尤其是套路。

你可能感兴趣的:(杂题集锦)