PKU演習問メモ(8/16)

No. 問題名 問題の種類および解法 難易度
3279 Fliptile 探索(ライツアウト) ★★☆☆☆
3256 Cow Picnic 深さ優先探索 ★★☆☆☆
3432 Count Squares 二分探索 ★★★☆☆
3665 iCow シミュレーション ★☆☆☆☆
3724 Find the parameter 深さ優先探索・誤差 ★★☆☆☆

3279 Fliptile

問題概要

mxnのライツアウトがある。
盤面をすべて0にするような解法で、手数が最小のものを、それが複数ある場合その中で辞書順で最小のものを求めよ。
解法が存在しない場合"IMPOSSIBLE"を出力せよ。

解法

定石どおり端の一列を2^m全て試す。後は一意に解法が定まる。
成功したら最短手数のものを覚える。

ソースコード
int dy[]={-1,0,1,0,0},dx[]={0,-1,0,1,0};
int h,w,cl[15][15];
int tmp[15][15],tmpa[15][15],tmpc;

void flip(int y,int x)
{
	tmpa[y][x]=1; tmpc++;
	rep(d,5)
	{
		int ny=y+dy[d],nx=x+dx[d];
		if(ny<0||ny>=h||nx<0||nx>=w)continue;
		tmp[ny][nx]^=1;
	}
}
bool cmp(int a[15][15],int b[15][15])
{
	rep(i,h)rep(j,w)if(a[i][j]!=b[i][j])return a[i][j]>b[i][j];
	return 0;
}

int main()
{
	scanf("%d%d",&h,&w);
	rep(i,h)rep(j,w)scanf("%d",cl[i]+j);
	
	int cnt=inf,ans[15][15];
	rep(bit,1<<w)
	{
		tmpc=0;
		rep(i,h)rep(j,w)tmp[i][j]=cl[i][j],tmpa[i][j]=0;
		rep(i,w)if(bit&1<<i)flip(h-1,i);
		
		for(int i=h-2;i>=0;i--)rep(j,w)
		if(tmp[i+1][j])flip(i,j);
		
		rep(i,w)if(tmp[0][i])goto NEXT;
		if(tmpc<cnt||tmpc==cnt&&cmp(ans,tmpa))
		{
			cnt=tmpc;
			rep(i,h)rep(j,w)ans[i][j]=tmpa[i][j];
		}
		NEXT:;
	}
	if(cnt==inf)puts("IMPOSSIBLE");
	else rep(i,h)rep(j,w)printf("%d%c",ans[i][j],j==w-1?'\n':' ');
	
	return 0;
}

3256 Cow Picnic

問題概要

k頭の牛が居る牧草地の番号がそれぞれ与えられる。
牧草地は全部でn個あり、それぞれはm本の一方通行の道で結ばれている。
これらの道が与えられたとき、全ての牛が到達可能な牧草地の数を求めよ。

k≦100,
n≦1000,
m≦10000を満たす。

解法

それぞれの牛の場所からDFSして、到達可能か見る。
最後に、全てから到達可能な点を数えて出力する解法で間に合う。
計算量はO((m+n)k).

ソースコード
int k,n,m,in[100];

vector<vi> e;
bool V[1001];
int cnt[1001];

void dfs(int c)
{
	V[c]=1;
	fr(i,e[c])if(!V[*i])dfs(*i);
}

int main()
{
	scanf("%d%d%d",&k,&n,&m);
	e.resize(n);
	rep(i,k)scanf("%d",in+i),in[i]--;
	rep(i,m)
	{
		int a,b; scanf("%d%d",&a,&b);
		e[a-1].pb(b-1);
	}
	
	rep(i,k)
	{
		rep(j,n)V[j]=0;
		dfs(in[i]);
		rep(j,n)cnt[j]+=V[j];
	}
	
	int ans=0;
	rep(i,n)if(cnt[i]==k)ans++;
	
	printf("%d\n",ans);
	
	return 0;
}

3432 Count Squares

問題概要

平面上にn個の点がある。
それらを4頂点とするような正方形の個数を求めよ。

n≦2000かつ、与えられる座標の全ての値は絶対値が10000以下の整数である。

解法

PKU 2002と全く同じ問題。
2点を適当に取ると、残りの2点の座標は定まるので、二分探索すればよい。

ソースコード
int n;
pi p[2000];

int main()
{
	scanf("%d",&n);
	rep(i,n)scanf("%d%d",&p[i].first,&p[i].second);
	sort(p,p+n);
	
	int cnt=0;
	rep(i,n)rep(j,i)
	{
		int dx=p[i].first-p[j].first,dy=p[i].second-p[j].second;
		if(binary_search(p,p+n,mp(p[i].first-dy,p[i].second+dx))&&
			binary_search(p,p+n,mp(p[j].first-dy,p[j].second+dx)))
		cnt++;
	}
	printf("%d\n",cnt/2);
	
	return 0;
}

3665 iCow

問題概要

音楽プレイヤーiCowは以下のアルゴリズムに従い曲のシャッフル順を決定する。

  • 全ての曲は初期レーティングR[i]がつけられている
  • 次に演奏される曲はレーティングが最大のものである(同じものがある場合その中で番号が最小のもの)
  • 曲が演奏された後、その曲のレートは0になり、その曲のレートは残りの曲に分配される
  • 上において等分ができないときは最初のほうから配られる曲に1ずつ多く分配される

このときシャッフル順で演奏される最初のT曲の番号を出力せよ。
曲数N≦1000,
T≦1000,
R[i]≦10000を満たす。

解法

与えられた条件をそのままシミュレーションすればよい。


USACOの入力データセットを見るに、n==1の場合はなく、場合分けをしなくともREになることはない……ような気がする。(試してない)

ソースコード
int n,t,r[1000];

int main()
{
	scanf("%d%d",&n,&t);
	rep(i,n)scanf("%d%",r+i);
	
	if(n==1)
	{
		rep(i,t)puts("1");
		return 0;
	}
	
	rep(i,t)
	{
		int p=max_element(r,r+n)-r;
		
		printf("%d\n",p+1);
		
		int one=r[p]/(n-1),re=r[p]%(n-1);
		rep(j,n)if(j!=p)
		{
			r[j]+=one;
			if(re>0)r[j]++,re--;
		}
		r[p]=0;
	}
	
	return 0;
}

3724 Find the parameter

問題概要

関数
y=exp(a0x0)+exp(a1x1)+……+exp(a9x9)
が通る点N個の座標が与えられる。

aiは1以上9以下の整数であり、全てのiに対してai≦ai+1を満たす。
このときa0〜a9の値を求めよ。

与えられる点のx座標は0以上5以下、N≦20としてよい。

解法

深さ優先探索で全通り試す。
残りを全て最小にしてもyを超える場合、あるいは残り全てを最大にしてもyに届かない場合を刈るような枝刈りを入れたが、なくても通る。


disscussで言われているように、誤差の扱いがおかしい模様。
EPS=1にして、最後の答えの判定を(int)(a*1000+0.5!=)にしたら通った。


long doubleもなしで通るみたい。ちょっと悪問?

ソースコード
typedef long double ld;
const ld EPS=1;

int n;
ld X[20],Y[20];
ld sum[20],lo[20][11],hi[20][11];
int a[10];

bool ck(int c)
{
	if(c==10)
	{
		rep(i,n)if((int)(sum[i]*1000+0.5)!=(int)(Y[i]*1000+0.5))return 0;
		return 1;
	}
	
	rep(i,n)
	{
		if(Y[i]>sum[i]+hi[i][c]+EPS)return 0;
		if(Y[i]<sum[i]+lo[i][c]-EPS)return 0;
	}
	return 1;
}
bool dfs(int c,int p)
{
	if(c==10)return 1;
	
	for(int i=p;i<=10;i++)
	{
		a[c]=i;
		rep(j,n)sum[j]+=exp(X[j]*i);
		if(ck(c+1)&&dfs(c+1,i))return 1;
		rep(j,n)sum[j]-=exp(X[j]*i);
	}
	return 0;
}

int main()
{
	cin>>n;
	rep(i,n)cin>>X[i]>>Y[i];
	rep(i,n)
	{
		lo[i][10]=hi[i][10]=0;
		for(int j=9;j>=0;j--)
		{
			lo[i][j]=lo[i][j+1]+exp(X[i]);
			hi[i][j]=hi[i][j+1]+exp(X[i]*10);
		}
		
		sum[i]=0;
	}
	dfs(0,1);
	rep(i,10)cout<<a[i]<<endl;
	
	return 0;
}