メモ

yukicoderでゆるふわgolf

yukicoder No.536 人工知能

問題はこちら
No.536 人工知能 - yukicoder

後ろ2文字が"ai"かどうかで分ける

char s[99];
int n;
main(){
	gets(s);
	n=strlen(s);
	if(s[n-2]=='a'&&s[n-1]=='i'){
		s[n-2]='A';
		s[n-1]='I';
	}else{
		s[n]='-';
		s[n+1]='A';
		s[n+2]='I';
	}
	puts(s);
}

で、この方針で縮めるとこうなって98B

char s[];
main(f){
	f=strlen(gets(s));
	strcmp(s+f-2,"ai")?s[f]=45,f+=3:0;
	s[f-2]=65,s[f-1]=73;
	puts(s);
}

でもsを編集するより、出力文字数をいじった方が短くなった

char s[];
main(f,g){
	f=strlen(gets(s));
	g=!strcmp(s+f-2,"ai");
	write(1,s,f-2*g);
	puts("-AI"+g);
}

91B
存在は知っていたが使ったことのなかったwrite関数を初めて使った。
文字列の途中までを字数を指定して出力するときに便利

yukicoder No.532 Possible or Impossible

問題はこち
No.532 Possible or Impossible - yukicoder

M+0*(残り) の形が作れれば十分
●M≧4のとき
M+(3-2-1)*(残り)
●M=3のとき
・N=3のとき:3*(2-1)
・N=4のとき:4+2-1*3
・N≧5のとき:3+(5-4-1)*(残り)
●M=2のとき
・N=2のとき:2*1
・N=3のとき:3-2+1
・N≧4のとき:2+(4-3-1)*(残り)
●M=1のとき
・N=1のとき:1
・N=2のとき:2-1
・N=3のとき:3-2*1
・N=4のとき:1*2+3-4
・N≧5のとき:1+(5-3-2)*(残り)
●M=0のとき
・N≦2のとき:無理
・N≧3のとき:(3-2-1)*(残り)

ということで、不可能なのはM=0かつN≦2のときのみ

n,m;
main(){
	scanf("%d%d",&n,&m);
	if(m==0 && n<=2)put("Impossible");
	else puts("Possible");
}

ぎゅ
定数2のところにscanfの返り値が使える

main(n,m){puts(scanf("%d%d",&n,&m)<n|m?"Possible":"Impossible");}

65B

yukicoder No.531 エヌスクミ島の平和協定

問題はこち
No.531 エヌスクミ島の平和協定 - yukicoder

m≧nなら全員で船に乗れば1日で移動できる。そうでないとする。
1回目の移動で船に乗るグループと乗らないグループに分けると、これはどちらも非空集合なので、動物1が船に乗り、動物2が船に乗らないとして一般性を失わない。
このとき「捕食者⇒被捕食者」の対偶「被捕食者でない⇒捕食者でない」から、動物3は船に乗る。
以下帰納的に奇数番目の動物は船に乗り、偶数番目の動物は乗らないことになる。
nが奇数のとき動物nが捕食者でありながら被捕食者でないのでダメ。
nが偶数のときグループ分けはうまくいくが、m<n/2のときは該当する動物が船に乗ることができないのでダメ。
よって「nが偶数かつm≧n/2」が可能であるための必要条件であり、実際にこのときグループ分けに従って、1日目に奇数番、2日目に偶数番の動物が移動することで2日で移動ができる。
m<nなので移動が可能ならば2日が最短である。

n,m,ans;
main(){
	scanf("%d%d",&n,&m);
	if(m>=n)ans=1;
	else if(n%2==0 && m>=n/2)ans=2;
	else ans=-1;
	printf("%d",ans);
}

ぎゅ

main(n,m){scanf("%d%d",&n,&m);printf("%d",n>m?n%2|m<n/2?-1:2:1);}

65B

yukicoder No.530 年齢って毎年変わるし覚えるの難しいよね

問題はこち
No.530 年齢って毎年変わるし覚えるの難しいよね - yukicoder

日付を気にしなくていいので、簡単

y;
main(){
	scanf("%d",&y);
	printf("%d",2017-y);
}

ぎゅ

n;main(){printf("%d",2017-atoi(gets(&n)));}

43B

yukicoder No.527 ナップサック容量問題

問題はこち
No.527 ナップサック容量問題 - yukicoder

まずは普通に01ナップサック問題を解く必要があるが、そのときのDPのやりかたとして2通り考えられる。

1つ目は、問題文の表にあるように、d[i]に「重さi以内で得られる価値の最大値」を保存する方法

#define max(p,q)(p>q?p:q)
d[200000];
v,w,n;
i,j,t,m;
main(){
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d%d",&v,&w);
		for(j=100000;j>=w[i];j--)d[j]=max(d[j],d[j-w]+v);
	}
	scanf("%d",&t);
	for(i=1;i<100000;i++)if(d[i]>=t){printf("%d\n",m=i);break;}
	for(;i<=100000;i++)if(d[i]>d[m]){printf("%d\n",i-1);return 0;}
	puts("inf");
}

もう1つは、d[i]に「価値iを得るための重さの最小値」を保存する方法
こちらは漸化式が見えるまで少し時間がかかった

#define INF (3LL<<29)
#define min(p,q)(p<q?p:q)
a[1<<17],*d=a+1000;//添字がマイナスになることがあるのでindexをずらした
v,w,n;
i,j,t;
main(){
	scanf("%d",&n);
	for(j=1;j<100010;j++)d[j]=INF;
	for(i=0;i<n;i++){
		scanf("%d%d",&v,&w);
		for(j=100010;j;j--)d[j]=min(d[j],d[j-v]+w);
	}
	scanf("%d",&t);
	printf("%d\n",d[t]?:1);
	printf(d[t+1]==INF?"inf":"%d",d[t+1]-1);
}


後者の方が出力が簡単なので、初期化を省略できればこちらのほうが短くなりそう。
初期化を省略すると0とINFの区別をうまくつけることが必要になるが、INF以外の値に更新されるのはΣvi以下に限るので、このことからつぎのように書ける。

a[1<<17],*d=a+1000;
v,w,n,s,m;
i,j,t;
main(){
	scanf("%d",&n);
	for(i=0;i<n;i++){
		scanf("%d%d",&v,&w);
		s+=v;
		for(j=s;j;j--)if(!d[j]|d[j]>d[j-v]+w)d[j]=d[j-v]+w;
	}
	scanf("%d",&t);
	printf(d[t+1]?"%d\n%d":"%d\ninf",d[t]?:1,d[t+1]-1);
}

入力の工夫

scanf("%d",&n);
for(i=0;i<n;i++){
	scanf("%d%d",&v,&w);
	s+=v;
	for(j=s;j;j--)if(!d[j]|d[j]>d[j-v]+w)d[j]=d[j-v]+w;
}
scanf("%d",&t);
//↑これが ↓こう
for(;~scanf("%d%d",&w,&t);){
	for(j=s;j;j--)if(!d[j]|d[j]>d[j-v]+w)d[j]=d[j-v]+w;
	s+=v=t;
}

その他の細々した工夫、ループ圧縮などを取り入れて

d[1<<17];
v,w,t;s,j;
main(m){
	for(;j?d[j]>(m=d[j>v?j-v:0]+w)|!d[j]?d[j]=m:0,j--:(j=s+=v=t,~scanf("%d%d",&w,&t));m=d[t+1]);
	printf(m?"%d\n%d":"%d\ninf",d[t]?:1,m-1);
}

161B