最小生成树的思维好题

来源:互联网 发布:棋盘分割算法优缺点 编辑:程序博客网 时间:2024/05/16 21:30

前言:

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

T1 Starway:

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

T2 货车运输:

就是我们要求路径上最短边权的最大值,那么就是一个最大生成树。建好图后在搞个Lca就好啦。顺便写一写倍增。
代码:

#include<cstdio>#include<algorithm>#include<cstring>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<cstdio>#include<algorithm>#include <queue>#include<cstring>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 <que> 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 <CD> 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 faint find(int limit){    bool vis[size][size];memset(vis,false,sizeof vis);vis[stx][sty] = true;    queue <que> 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;}

结语

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

阅读全文
0 0
原创粉丝点击