PKU演習問メモ(4/24)

No. Title 分類・解法 主観的難易度
2996 Help Me with the Game 実装問題 ★★☆☆☆
2993 Emag eht htiw Em Pleh 実装問題 ★☆☆☆☆
3044 City Skyline データ構造 ★★☆☆☆
2769 Reduced ID Numbers 整数問題 ★★☆☆☆


以下問題概要解法ソースコード

2996 Help Me with the Game

問題概要

チェスの盤面が与えられる。この盤面を読み取り、各プレイヤーの各駒がどこに位置するかを文字列の形で出力せよ。


ただし、各駒の位置は左端をa列、最下段を1段とする座標系を使い、
各駒はKa1のように「駒を表す1文字」+座標と表し、(ただしポーンの識別文字は省略する)、各駒の文字列は,で区切る。


駒はランクの高い順(K>Q>R>B>N>P)に並べ、同じ駒が複数ある場合、黒のプレイヤーは段の数字が大きい順に、白のプレイヤーは段の数字が小さい順になるように駒を並べる。それでも順が決まらないときは、列を表すアルファベットが昇順になるように並べる。

解法

条件にしたがって実装すればよいのだが、並び順に関する条件指定が多いため、間違えないように気をつける。以下無駄にクラスを使った実装例。。。

ソースコード
int rank[256];

bool cmp(const string &l,const string &r)
{
	return rank[l[0]]>rank[r[0]];
}

struct pclst{
	vector<string> notpwn;
	vector<string> pwn;
	void put()
	{
		int n=notpwn.size(),m=pwn.size();
		stable_sort(all(notpwn),cmp);
		
		rep(i,n)cout<<notpwn[i]<<(i==n-1?"":",");
		if(n&&m)cout<<",";
		rep(i,m)cout<<pwn[i]<<(i==m-1?"":",");
	}
	void push(char p,int y,int x)
	{
		string pos; pos.pb('a'+x); pos.pb('1'+y);
		if(p=='p')pwn.pb(pos);
		else
		{
			p+='A'-'a';
			notpwn.pb(p+pos);
		}
	}
};

int main()
{
	rank['K']=5,rank['Q']=4,rank['R']=3,rank['B']=2,rank['N']=1;
	pclst bl,wh;
	
	vector<string> str(16);
	rep(i,16)cin>>str[i];
	
	rep(i,8)rep(j,8)
	{
		char p=str[15-2*i][2+j*4];
		if('A'<=p&&p<='Z')wh.push(p-'A'+'a',i,j);
	}
	for(int i=7;i>=0;i--)rep(j,8)
	{
		char p=str[15-2*i][2+j*4];
		if('a'<=p&&p<='z')bl.push(p,i,j);
	}
	
	cout<<"White: "; wh.put(); cout<<endl;
	cout<<"Black: "; bl.put(); cout<<endl;
	
	return 0;
}

2993 Emag eht htiw Em Pleh

問題概要

2996の逆。チェスの駒を表す文字列が与えられるので、対応する盤面を表示する。

解法

計算時間に余裕があるのでふんだんにvectorコンテナやstringコンテナなどを使う。

ソースコード
int main()
{
	vector<string> ans(17);
	rep(i,9)
	{
		rep(j,8)ans[i*2]+="+---";
		ans[i*2].pb('+');
	}
	rep(i,8)
	{
		rep(j,8)ans[i*2+1]+=(i&1^j&1)?"|:::":"|...";
		ans[i*2+1].pb('|');
	}
	
	string str;
	rep(i,2)
	{
		cin>>str; cin>>str;
		fr(it,str)if(*it==',')*it=' ';
		stringstream ss(str);
		while(ss>>str)
		{
			int l=str.size(),y=str[l-1]-'1',x=str[l-2]-'a';
			char p=l==3?str[0]:'P';
			if(i)p+='a'-'A';
			
			ans[15-2*y][4*x+2]=p;
		}
	}
	
	rep(i,17)cout<<ans[i]<<endl;
	
	return 0;
}

3044 City Skyline

問題概要

ビル群のシルエットが、その角の座標によって与えられる。
このとき、このシルエットが出来るために必要な最小限のビルの数を求めよ。


角の座標のリストはx座標が小さい順に並んでいる。

解法

ビルがなければならないのは、

  • 凸な角が出現したとき
  • 凹な角が出現し、それより前の角でそのy座標よりも低い辺が最後に出現したよりも後のものに、同じ高さの角が出現してないとき

である。


これはスタックを用いて実装すればよい。

ソースコード
int main()
{
	int n,w;
	scanf("%d%d",&n,&w);
	
	int ans=0,x,y;
	stack<int> S;
	rep(it,n)
	{
		scanf("%d%d",&x,&y);
		if(S.empty()||y>S.top())
		{
			if(y>0)ans++;
			S.push(y);
		}
		else
		{
			while(!S.empty()&&S.top()>y)S.pop();
			if(S.empty()||y!=S.top())if(y>0)ans++;
			S.push(y);
		}
	}
	cout<<ans<<endl;
	
	return 0;
}

2769 Reduced ID Numbers

問題概要

生徒にはユニークな1以上10^6未満の学生番号がある。
g人の生徒からなるグループを、れぞれの生徒の学生番号をmで割った余りを全て異なる値になるよう作りたい。
g人の学生番号が与えられたとき、このような最小のmを求めよ。

解法

a%m=b%mはa-bがmで割り切れることと同値。
したがって、どの二つの学生番号の差も割り切らないような最小のmが求める値である。


mの候補は100万以下なので全て覚えながら、候補としてありえないものを消していけばいい。このO(g^2*(IDの差の最大値)^1/2)の解法で計算時間も間に合う。

ソースコード
bool ok[1000000];

int main()
{
	int cs; cin>>cs;
	while(cs--)
	{
		fill(ok,ok+1000000,1);
		
		int g,s[300];
		cin>>g;
		rep(i,g)
		{
			cin>>s[i];
			rep(j,i)if(s[j]>s[i])
			{
				int d=s[j]-s[i];
				for(int p=1;p*p<=d;p++)
				if(d%p==0)ok[p]=ok[d/p]=0;
			}
		}
		int ans=1;
		while(!ok[ans])ans++;
		cout<<ans<<endl;
	}
	return 0;
}