2010 ICPC国内予選 D.ぐらぐら

問題概要

テトリスのようにピースを積み重ねてオブジェを作るとき、そのオブジェが安定かどうかを判定せよ。


ただしオブジェが安定であるとは、全てのピースに対して、
そのピースおよびそのピースが支える全てのピースをあわせた重心が、そのピースと、それを支えるピースの接する部分のうち、最も左のx座標から最も右のx座標の間に含まれることをいう。(境界は含まない)

  • 最も下のピースは地面に接している。
  • ピースとピースは面がぴったりくっついている。
  • ピースは樹状に積み重ねられている。

ことを仮定してよい。

解法

ピース同士で、あるピースがあるピースを支えているという関係を有向グラフにする。
そして地面に接するピースから、深さ優先探索で「各ピースおよびそのピースが支える全てのピースをあわせた」重心の位置とピースのサイズを求める。
そして深さ優先探索で全てのピースが安定かどうかを判定すればよい。

ソースコード

int dx[]={-1,0,1,0},dy[]={0,-1,0,1};

int w,h;
char in[100][100];

bool e[1000][1000];
int mnx[1000],mxx[1000];
int id[100][100];
int npc,sz[1000];
double xsum[1000];

void nul(int y,int x,int num,char bl)
{
	xsum[num]+=x+0.5;
	sz[num]++;
	
	rep(d,4)
	{
		int ny=y+dy[d],nx=x+dx[d];
		if(ny<0&&nx<0||ny>=h||nx>=w||in[ny][nx]!=bl)continue;
		if(id[ny][nx]>=0)continue;
		id[ny][nx]=num; nul(ny,nx,num,bl);
	}
}
void getsize(int num)
{
	rep(i,npc)if(e[num][i])
	{
		getsize(i);
		sz[num]+=sz[i];
		xsum[num]+=xsum[i];
	}
}

bool stable(int num)
{
	rep(i,npc)if(e[num][i])
	if(!stable(i))return 0;
	if(xsum[num]/sz[num]<=mnx[num]||mxx[num]<=xsum[num]/sz[num])return 0;
	
	return 1;
}

int main()
{
	while(cin>>w>>h,w){
		rep(i,h)cin>>in[i];
		
		npc=0;
		rep(i,h)rep(j,w)id[i][j]=-1;
		
		rep(i,h)rep(j,w)if(id[i][j]<0&&in[i][j]!='.')
		{
			id[i][j]=npc;
			sz[npc]=xsum[npc]=0;
			nul(i,j,npc,in[i][j]); npc++;
		}
		
		rep(i,npc)
		{
			rep(j,npc)e[i][j]=0;
			mnx[i]=inf,mxx[i]=-inf;
		}
		rep(i,h-1)rep(j,w)if(id[i+1][j]!=id[i][j])
		{
			int d=id[i+1][j],u=id[i][j];
			if(d<0||u<0)continue;
			
			e[d][u]=1;
			mnx[u]=min(mnx[u],j);
			mxx[u]=max(mxx[u],j+1);
		}
		rep(i,w)if(id[h-1][i]>=0)
		{
			int d=id[h-1][i];
			mnx[d]=min(mnx[d],i);
			mxx[d]=max(mxx[d],i+1);
		}
		
		int root=0;
		for(;root<npc;root++)
		{
			rep(i,npc)if(e[i][root])goto NEXT;
			break; NEXT:;
		}
		getsize(root);
		cout<<(stable(root)?"STABLE":"UNSTABLE")<<endl;
	}
	return 0;
}