読者です 読者をやめる 読者になる 読者になる

メモ

yukicoderで遊んでいる競プロゆるふわ勢

yukicoder No.10 +か×か

問題はこちら
No.10 +か×か - yukicoder


メモ化再帰
「i番目までで数sが作れた」という情報を保存することで2^(N-1)とおり全てを試す必要はなくなる

x[60][100100],a[60],m,n;
char p[60];
void f(int i,int s){
	//今i番目まで使ってsを作った、という状態
	if(i==n&&s==m)puts(p+1),exit(0);
	//すべての数を使い尽くして、かつ、目標値になったなら出力して終わり
	if(s>m||i==n||x[i][s]++)return;
	//目標値を超えたり、数を使い尽くしても目標値にならなかったり、ここに来たことがあればこの先を調べる必要はない
	p[i]='+',f(i+1,s+a[i]);
	p[i]='*',f(i+1,s*a[i]);
}

int main(){
	int i;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)scanf("%d",a+i);
	f(1,a[0]);
}

全部配列にまとめて読み込む。1つ目の情報はいらないので消えてもらう
あとは三項演算子でまとめつつ、「引数は後ろから評価される」「計算式は前から評価される」という処理系依存テクニックでまとめる
(p[i]への書き込みが2度あるので正確には未定義動作?)

x[60][1<<20],a[60];
char p[60];
f(i,s){
	a[i]?s>*a|x[i][s]++||f(i+1,s+a[i],p[i]=43)+f(i+1,s*a[i],p[i]--):s-*a?:exit(!puts(p+2));
}
main(i){
	for(;~scanf("%d",a-i--););
	f(2,a[1]);
}

2次元配列を1次元化し、その配列に全部読み込むことにする
さらによく考えたらf(2,a[1])ではなく、f(1,0)でよい(足し算が行われf(2,a[1])が呼ばれる)

x[1<<26];
char p[60];
f(i,s){
	x[i]?s>*x|x[i<<20|s]++||f(i+1,s+x[i],p[i]=43)+f(i+1,s*x[i],p[i]--):s-*x?:exit(!puts(p+2));
}
main(i){
	for(;~scanf("%d",x-i--););
	f(1,0);
}

160B

16/10/11追記
exitを使わずとも、*xを適当な値で潰してしまえば、それ以降出力されることはなくなるので、putsの返り値を使うことにして次のようにできる

x[1<<26];
char p[60];
f(i,s){
	x[i]?s>*x|x[i<<20|s]++||f(i+1,s+x[i],p[i]=43)+f(i+1,s*x[i],p[i]--):s==*x?*x=~puts(p+2):0;
}
main(i){
	for(;~scanf("%d",x-i--););
	f(1,0);
}

159B
~putsじゃなくてputsでもごまかせないかと思ったけど05.txtで落とされる