# -*- coding: utf-8 -*- #Python 2.7.5 (v2.7.5:ab05e7dd2788, May 13 2013, 13:18:45) #CodeIQのPythonで島の数をカウントしようっていう問題。 #https://codeiq.jp/ace/joboffer_apli/q377 #最初に自分で考えたアルゴリズムが #アルゴリズムその1:import_island、island_count、__joinlist #です。これは隣接リストから積集合と和集合を使って島の数をカウントするんですが #白石と黒石の数が増えると極端にパフォーマンスが低下します。 #そこでインターネッツの力を借りて再度考えたアルゴリズムが #アルゴリズムその2:import_island2、island_count2、__check_replace #となっています。 #このアルゴリズム2はアルゴリズム1と違って石の数を全部走査すれば終わるので線形時間で終わります。 #パフォーマンス比較結果: #石の総数:100*100=10000、黒石の数:4987、島の数:703 #import_islandの実行時間:24秒227035 #import_island2の実行時間:0秒066305 #そもそもクラスの書き方に自信がないでござる。 #テキストファイルは自分で用意してください。 import math, datetime class IslandCount(object): def __init__(self,ff='sample_island1.txt'): self.ff=ff #白石0黒石1で描画したテキストファイル #石を描画する。白:0、黒:1。 def draw_island(self,c=[],m=0,n=0): list1=[] b='' for i in range(m*n): list1.append('0') for i in c: list1[i-1]='1' for i in range(m-1): list1.insert((i+1)*(n+1)-1,'\n') for i in range(len(list1)): b+=str(list1[i]) print b ''' アルゴリズムその1ここから ''' #txtファイルを読み込む場合に使用する。 def import_island(self,f=''): f=self.ff flist=[] sindex=[] j=0 flg=0 f=self.ff f=open(f).read() for i in f: j+=1 if i!='\n': flist.append(i) else: if flg==0: flg=1 clmns=j-1 lns=len(flist)/clmns for i, v in enumerate(flist): if v=='1': sindex.append(i+1) return self.island_count(sindex,lns,clmns) #島の数を返す。 def island_count(self,a=[],m=0,n=0): count=0 join_list2=[] new_list=[] flist=[] l=len(a) newlist2=[] #n=int(math.sqrt(m)) #黒石の1方向隣接リストを生成する。 #例:[1,2],[2,3],[2,8] for i in range(l-1): for j in range(l-1): if a[j+1]-a[i]==1 and a[i]%n!=0 or a[j+1]-a[i]==n: join_list2+=[[a[i],a[j+1]]] #黒石の隣接リストで結合できるものを結合する。 #例:[1,2,3,8] for i in range(len(join_list2)): for j in range(len(join_list2)): if join_list2[i][1]==join_list2[j][0]: join_list2[i]=join_list2[i]+join_list2[j][1:] for i in range(len(join_list2)): join_list2[i]=sorted(join_list2[i]) #結合した隣接リストから島リストを返す。 #例:[1,2,3,8],[7,8,9,10],[3,4,10]→[1,2,3,4,7,8,9,10] newlist2=self.__joinlist(join_list2) #島リストを結合する。 #[[6, 12], [7, 13]]→[6, 12, 7, 13] for i in newlist2: flist+=i #黒石の数-結合した島リストの要素数=黒石1つだけの孤島 #求めたい島の数=孤島+島リストの数(孤島ではない島の数) count=l-len(flist)+len(newlist2) '''描画が不要な場合は1行下ののコメント化してください。''' self.draw_island(a,m,n) return count #黒石の隣接リスト同士にて積集合が存在する場合、和集合を生成し重複を削除する。 def __joinlist(self,join_list2): newlist=[] flg=0 for i in range(len(join_list2)): for j in range(len(join_list2)): #自分自身以外で積集合が空でない場合 if i!=j and join_list2[j]!=[]: if len(set(join_list2[i])&set(join_list2[j]))>0: #和集合 flg=1 join_list2[i]=list(set(join_list2[i])|set(join_list2[j])) join_list2[j]=[] #重複を削除する。 for line in join_list2: if sorted(line) not in newlist and line!=[]: newlist.append(sorted(line)) #積集合がすべて空の場合、再帰を抜ける。 if flg==0: return newlist return self.__joinlist(newlist) ''' アルゴリズムその1ここまで ''' ''' アルゴリズムその2ここから ''' #txtファイルを読み込む場合に使用する。Part2 def import_island2(self,f=''): f=self.ff linescount=0 c=[] for line in open(f): b=[] columnscount=0 for char in line: if char.strip()!='': columnscount+=1 b+=[int(char.strip())] linescount+=1 c+=[b] return self.island_count2(c,linescount,columnscount) #島の数を返す。Part2 def island_count2(self,c,m,n): r=[0,0] for i in range(m): for j in range(n): if c[i][j] != -1: #c[i][j]==0の場合、r[0]+=1 #c[i][j]==1の場合、r[1]+=1 r[c[i][j]] += 1 #上で+1した後、c[i][j]の上下左右をチェックしていき同値の場合-1に置換する #即ち指定したc[i][j]と同じ島はカウントに+1後、すべて-1に置換され、次のチェックでカウントされない self.__check_replace(c,i,j,m,n) return r[1] #黒石の島の数を返す。白石はr[0] #チェックした要素と隣接する同値の要素を-1で再帰的に置換していく。 def __check_replace(self,c,i,j,m,n): k = c[i][j] c[i][j] = -1 #2行目以降で上の石が同じ場合、上の石に対して再帰 if i>0 and c[i-1][j]==k: self.__check_replace(c,i-1,j,m,n) #2列目以降で左の石が同じ場合、左の石に対して再帰 if j>0 and c[i][j-1]==k: self.__check_replace(c,i,j-1,m,n) #最終行より前の行で下の石が同じ場合、下の石に対して再帰 if i self.__check_replace(c,i+1,j,m,n) #最終列より前の列で右の石が同じ場合、右の石に対して再帰 if j self.__check_replace(c,i,j+1,m,n) ''' アルゴリズムその2ここまで ''' if __name__=='__main__': ''' a1:黒石のインデックス b1,c1:縦マスの数,横マスの数 f1-f4:黒石を1白石を0で記述したテキストファイル ''' a1=[6,7,12,13] b1,c1=6,6 f1='sample_island1.txt' a2=[6,11,12,17] b2,c2=6,6 f2='sample_island2.txt' a3=[1,2,3,4,7,8,9,10,12,13,24,25,29,30,31,35,36] b3,c3=6,6 f3='sample_island3.txt' a4=[1,2,3,4,7,8,9,10,12,13,24,25,29,30,31,35,36] b4,c4=7,7 f4='sample_island_100_100.txt' aaa=IslandCount(f1) #print '島の数:'+str(aaa.island_count(a1,b1,c1)) #黒石のインデックス数と石の総数から島の数を返す。 t1=datetime.datetime.now() print '島の数:'+str(aaa.import_island())+' #アルゴリズム1' #filenameで指定されたファイルから島の数を返す。1番目のアルゴリズム t2=datetime.datetime.now() print str(t2-t1)+'秒' t1=datetime.datetime.now() print '島の数:'+str(aaa.import_island2())+' #アルゴリズム2' #filenameで指定されたファイルから島の数を返す。2番目のアルゴリズム t2=datetime.datetime.now() print str(t2-t1)+'秒' print '===========================================================================================' bbb=IslandCount(f2) #print '島の数:'+str(bbb.island_count(a2,b2,c2)) t1=datetime.datetime.now() print '島の数:'+str(bbb.import_island())+' #アルゴリズム1' t2=datetime.datetime.now() print str(t2-t1)+'秒' t1=datetime.datetime.now() print '島の数:'+str(bbb.import_island2())+' #アルゴリズム2' t2=datetime.datetime.now() print str(t2-t1)+'秒' print '===========================================================================================' ccc=IslandCount(f3) #print '島の数:'+str(ccc.island_count(a3,b3,c3)) t1=datetime.datetime.now() print '島の数:'+str(ccc.import_island())+' #アルゴリズム1' t2=datetime.datetime.now() print str(t2-t1)+'秒' t1=datetime.datetime.now() print '島の数:'+str(ccc.import_island2())+' #アルゴリズム2' t2=datetime.datetime.now() print str(t2-t1)+'秒' print '===========================================================================================' ddd=IslandCount(f4) #print '島の数:'+str(ddd.island_count(a4,b4,c4)) t1=datetime.datetime.now() print '島の数:'+str(ddd.import_island())+' #アルゴリズム1' t2=datetime.datetime.now() print str(t2-t1)+'秒' t1=datetime.datetime.now() print '島の数:'+str(ddd.import_island2())+' #アルゴリズム2' t2=datetime.datetime.now() print str(t2-t1)+'秒' print '==========================================================================================='