ACM-ICPC 2010 国内予選問題 Problem D ぐらぐら

Problem D ぐらぐら

問題文の通りに計算するだけ。だが、面倒くさい。

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    while ( true )
    {
        //  入力
        int w, h;
        cin >> w >> h;
        if ( w == 0  &&  h == 0 )
            break;

        vector<string> elev( h );
        for ( int i=0; i<h; i++ )
            cin >> elev[i];

        //  ピースを探す
        int num = 0;                //  ピースの個数
        vector<vector<int> > bx;    //  ブロックのx座標
        vector<vector<int> > by;    //  ブロックのy座標

        while ( true )
        {
            //  まだピースに割り当てられていないブロックを探す
            char t = '.';
            for ( int y=0; y<h && t=='.'; y++ )
            for ( int x=0; x<w && t=='.'; x++ )
                if ( elev[y][x] != '.' )
                    t = elev[y][x],
                    elev[y][x] = '*';
            if ( t == '.' )
                break;

            //  隣接しているブロックを'*'に置換
            bool up;
            do
            {
                up = false;
                for ( int y=0; y<h; y++ )
                for ( int x=0; x<w; x++ )
                if ( elev[y][x] == '*' )
                {
                    if ( 0<x   && elev[y][x-1]==t )  elev[y][x-1]='*', up=true;
                    if ( 0<y   && elev[y-1][x]==t )  elev[y-1][x]='*', up=true;
                    if ( x<w-1 && elev[y][x+1]==t )  elev[y][x+1]='*', up=true;
                    if ( y<h-1 && elev[y+1][x]==t )  elev[y+1][x]='*', up=true;
                }
            }
            while ( up );

            //  '*'の座標をピースに割り当てて、'.'に置換
            bx.push_back( vector<int>() );
            by.push_back( vector<int>() );
            
            for ( int y=0; y<h; y++ )
            for ( int x=0; x<w; x++ )
            if ( elev[y][x] == '*' )
            {
                bx[num].push_back( x );
                by[num].push_back( y );
                elev[y][x] = '.';
            }

            num++;
        }

        //  支えられている最左・最右のブロックと支えているピースを計算
        vector<int>         xL( num, w );       //  他ピースに接している最左のブロック
        vector<int>         xR( num, 0 );       //  他ピースに接している最右のブロック
        vector<int>         parent( num, -1 );  //  このピースを支えているピース(-1:地面)
        vector<vector<int> >child( num );       //  このピースが支えているピース

        for ( int p=0; p<num; p++ )
        {
            for ( int b=0; b<4; b++ )
            {
                int s = -1;
                for ( int p2=0; p2<num; p2++ )
                for ( int b2=0; b2<4; b2++ )
                    if ( p2 != p  &&
                         bx[p][b]   == bx[p2][b2]  &&
                         by[p][b]+1 == by[p2][b2] )
                        s = p2;

                if ( s != -1  ||
                     by[p][b] == h - 1 )
                {
                    xL[p] = min( xL[p], bx[p][b] );
                    xR[p] = max( xR[p], bx[p][b] );
                    parent[p] = s;
                }
            }

            if ( parent[p] != -1 )
                child[parent[p]].push_back( p );
        }

        //  重心を計算
        vector<int>     Mn( num, -1 );  //  このピースと支えているピースのブロックの個数(-1:未計算)
        vector<int>     Mx( num, 0  );  //  このピースと支えているピースのブロックのx座標の合計

        bool end = false;

        while ( ! end )
        {
            for ( int p=0; p<num; p++ )
            {
                //  支えているピースが計算済みのピースから求めていく
                bool f = true;
                for ( int c=0; c<(int)child[p].size(); c++ )
                    if ( Mn[child[p][c]] == -1 )
                        f = false;

                if ( f  &&  Mn[p] == -1 )
                {
                    Mn[p] = 0;
                    Mx[p] = 0;

                    for ( int c=0; c<(int)child[p].size(); c++ )
                        Mn[p] += Mn[child[p][c]],
                        Mx[p] += Mx[child[p][c]];

                    for ( int b=0; b<4; b++ )
                        Mn[p]++,
                        Mx[p] += bx[p][b];

                    //  地面に接しているピースの重心が求まれば終了
                    if ( parent[p] == -1 )
                        end = true;
                }
            }
        }

        //  安定性判定
        bool stable = true;

        for ( int p=0; p<num; p++ )
        {
            //  Mx[p]/Mn[p] <= xL[p] - 1/2  ||
            //  Mx[p]/Mn[p] >= xR[p] + 1/2
            if ( Mx[p]*2 <= xL[p]*Mn[p]*2 - Mn[p]  ||
                 Mx[p]*2 >= xR[p]*Mn[p]*2 + Mn[p] )
                stable = false;
        }

        if ( stable )
            cout << "STABLE" << endl;
        else
            cout << "UNSTABLE" << endl;
    }
}