C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[32]: プログラムの練習問題をゆっくりと考えていきます [1]


(過去ログ 105 を表示中)

[トピック内 37 記事 (21 - 37 表示)]  << 0 | 1 >>

■62879 / inTopicNo.21)  Re[17]: プログラムの練習問題をゆっくりと考えていきます
  
□投稿者/ 堀江伸一 (71回)-(2011/11/07(Mon) 17:49:34)
うーん、i,x,yの条件はなんだか複雑なので保留。
そのうち考えるということにします。
誰か返答でもあればいいのですが、考えつくか返答があるまで保留です。



少し趣向を変えて別の問題を考えます。
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0550
この問題がなかなか難しい。

普通に二つに分けるだけでも大変なのに、それに切断にかかる時間まで計算に入れないといけない。
切断箇所は999か所、切断するしないで2^999。

何かコロンブスの卵的問題のように思えます。
正面から挑むと組み合わせ数の海に溺れてしまいます。

逆から発想してみるというのはどうだろう?

全てが切断された状態からコネクトしていく逆再生なら何か手が出るかもしれません。
切断という作業は関数でいえば1対多なのかもしれません。
f(x)=yであるyになるxが複数あるように、切断するという発想でとらえると先が多になってしまう。

逆関数は多に対し1つしか決まらないということで枝から幹へ辿るようなことが可能かもしれません。
コネクトという発想でとらえたらうまくいくかもしれません、いかないかもしれません。

こう言った組み合わせの問題は数学の世界で見るとグラフに翻訳できることも多いようですが、グラフに直すならどのような形でグラフにするか、どこからアプローチするか?

そういう視点で見ると面白い問題なのかもしれません。
引用返信 編集キー/
■62884 / inTopicNo.22)  Re[18]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ shu (1075回)-(2011/11/08(Tue) 07:40:40)
2011/11/08(Tue) 11:53:18 編集(投稿者)
No62879 (堀江伸一 さん) に返信

2分割
a | b  => a= b
  p1

3分割
a | b | c => b = a+c = N/2
  p2  p3        p2,p3はp1ではない(p1による2分割が最小であることに矛盾する
                 というかp1は中央なので2分割以外ありえない)


4分割
a | b | c | d   => a+c=b+d=N/2
  p4  p5  p6      p4,p5,p6はp1ではない、かつ2点以上がp2,p3と一致することはない(同様)


たぶんこんな感じのことが成立すると思われる。検証してません。

引用返信 編集キー/
■62923 / inTopicNo.23)  Re[19]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (72回)-(2011/11/10(Thu) 15:18:08)
2011/11/10(Thu) 15:32:36 編集(投稿者)
2011/11/10(Thu) 15:31:33 編集(投稿者)

ありがとうございます。
shuさんのはちょっと難しそうなのでプリントして今からよく考えてみます。


私が思いついた解法としては。
まずこの問題は動的計画法もしくは特定のデータを優先して処理することで解決できる問題のようです。
正答者データにあるコード実行時間を考慮すると線形的な解はなさそうですね。


計算量の安定する動的計画法を採用します。

まず、組み合わせ数を減らします。

ある切断方法がある場合切断位置をA1,A5,A4と切断するのも
A1 A4 A5と切断するのもタイムに変わりはありません。

よって左端から切断していくこととして組み合わせ数を減らします。

次に、左端から切断していった場合二人をAさんBさんとします。
切断したものを左から見てAさんBさんの分配を見ていくと
ABABA,,,という順番でしか分けられないことが判明します。

もし
ABBAのような分配があればBBは切断する必要がありません。
よってABAB、、、のような配置となります。

左から切断していってAさんBさんと切断した端から二人に分けていった場合r個目の切断位置がAiだったとします。

するとAiまでで何か所どこを切断したかという情報は必要なくなります。

必要な情報はAさんが何個でBさんが何個で今どの部分の切断箇所を分離したかという情報とここまでの切断タイムと次の切断をもらうのがAさんかBさんかだけが重要となります。

@Ai個目を切断したときAさんがK個でBさんがi-k個でタイムはtで次の切断をもらうのがAさんかBさんか。

後は@が同じ条件になる別の切り方の中で一番タイムの短い切り方以外残す必要がありません。
動的計画法で片が付き計算量は大雑把に見積もって5000*10000*2の1億回、実際はもう少し減らせるまで圧縮できます。
この計算回数はぎりぎり現実的なタイムで合格できそうです。

この発想の場合思考力と分析力さえあれば高校生でも解ける問題であることが判明しました。

後はコードを書いてこの発想が正しいか試してみるだけです。
引用返信 編集キー/
■62928 / inTopicNo.24)  Re[20]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (73回)-(2011/11/10(Thu) 17:45:41)
上記の考え方をそのまま実装してみたところコード実行速度5位メモリ使用量低で正答できました。
ここから先の高速化とかはあまり考えてないので。
もし高速化できそうなところがあったらご指摘ください。


#include<stdio.h>
#include<string.h>

const int A=0;//二人をAさんBさんとする
const int B=1;

void setData(int n){
	int memo[2][5002];//[次にもらうのがAさん=0 Bさん=1][Aさんがここまででもらった個数]=最小切断時間
	int next[2][5002];//次の切断箇所の検討
	int cutTimes[10001];//切断時間
	int cutTime,herf=n/2,nowTime;
	int up;

	for(int i=0;i<n-1;i++)scanf("%d",&cutTimes[i]);
	
	memset(memo,127,sizeof(memo));//足される切断秒数は10000なのでオーバーフローしない
	memo[A][1]=0;//計算しやすいようAさんが1個目を取得した状態から始める
	
	for(int turn=0;turn<n-1;turn++){
		memset(next,127,sizeof(next));//ダミーデータ
		cutTime=cutTimes[turn];//切断箇所をずらしていく

		up=turn+1>herf?herf:turn+1;//上限
		for(int k=1;k<=up;k++){
			nowTime=memo[A][k];//Aさん
			
			//切断せずAさんが続けてもらう
			next[A][k+1]=nowTime;
			
			//切断して次はBさんがもらう
			next[B][k]=nowTime+cutTime;
		}
		for(int k=1;k<=up;k++){
			//Bさんの番
			nowTime=memo[B][k];
			//Bさんが続けてもらう
			next[B][k]=next[B][k]>nowTime?nowTime:next[B][k];
			
			//Aさんがもらう
			nowTime+=cutTime;
			next[A][k+1]=next[A][k+1]>nowTime?nowTime:next[A][k+1];
		}
		memcpy(memo,next,sizeof(next));
	}
	nowTime=memo[A][herf]>memo[B][herf]?memo[B][herf]:memo[A][herf];
	
	printf("%d\n",nowTime);
}

int main(){
	int n;
	scanf("%d",&n);
	setData(n);
	while(scanf("%d",&n)!=EOF){
	}
}

引用返信 編集キー/
■62930 / inTopicNo.25)  Re[21]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (74回)-(2011/11/10(Thu) 18:32:11)
shuさんの方法だと組み合わせ数を抑える方法がないような気がします。
よろしければもう少し詳細な考え方や利点をご提示いただけないでしょうか?


お返事をお待ちしながら次の問題はこれです。
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0553
RPGのダンジョンもぐりを題材にした問題で、ダンジョンの階層を降りるごとに指定されただけHPが減り地下N回にある宝箱まで到達せよという問題です。
HP0以下にならないようにするため途中各階にある回復の泉で回復するのですが、回復回数を最小に抑えたときの回数を計算します。

前回と同じく動的計画法で解けそうに思われますがHPや階数の値が大きいために動的計画法でも単純にはうまくいきそうもありません。

とりあえず検討をたててみます。

HPを減らしながら各階を降りて行き、HPが0以下になったら前の階に巻き戻って実は回復の泉で回復していたことにすれば計算量を抑えられそうです。

ただしこの場合HPが最大値以上に回復しない。
という部分がネックになります。

その階で回復したことにする時のHPをきちんと覚えておく必要がありますし、そこで回復していたことにすれば回復した次の階では回復した分だけHPを上昇させて計算しなければなりません。

その辺を考慮するとなると少しめんどくさそうな問題に見えます。
引用返信 編集キー/
■62937 / inTopicNo.26)  Re[22]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ shu (1081回)-(2011/11/10(Thu) 22:05:50)
No62930 (堀江伸一 さん) に返信
> shuさんの方法だと組み合わせ数を抑える方法がないような気がします。
> よろしければもう少し詳細な考え方や利点をご提示いただけないでしょうか?
>
>
最適な案かどうかは分かりません。
2分割しp1の時間が最少ということから次に探すのはp1以上の切断ポイントを除いたところである。
3分割しp1+p2(<p1なら求める最少値を更新)。p1+p2以上およびp1+p2<pi+pjとなるポイントpjがないpiのすべてを除いたところになる。
これはpjを残っているポイントのうち最少時間のポイントとしたときpi>p1+p2-pjという判定で可能
m分割のときp1+...+pm-1.この最小値以上のポイントとpi>p1+pm-1-(残ポイントのうち少ない時間順にm-2ポイント足したもの)
残ポイント数で分割が不可能になったらそこで終了。終了条件はもっとよいものがあるかもしれません。
引用返信 編集キー/
■62955 / inTopicNo.27)  Re[23]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (75回)-(2011/11/12(Sat) 19:16:47)
最初真ん中で割る2分割を試しこのタイムをP1とする。
3分割を試すときはP1より短いタイムで切断できるポイントしか試せない。
確率的にはP1より大きい半分の分割点が削除されますね。

3分割点をP2,P3とするならP2+P3<P1でなくてはいけない
この条件を満たさない分割点は消去する。

次は同じノリで4分割点となるのでしょうか?
4分割点3か所をP4,P5,P6とすると
P4+P5+P6<p2+p3

確かにこれはいい方法ですね。
組み合わせ数が増えたとき点が効率よく削除されます。

2分割で18
3分割で30
4分割で50
5分割で10
になるような場合大丈夫でしょうか?
引用返信 編集キー/
■62956 / inTopicNo.28)  Re[24]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ shu (1082回)-(2011/11/12(Sat) 21:03:31)
No62955 (堀江伸一 さん) に返信

> 2分割で18
> 3分割で30
> 4分割で50
> 5分割で10
> になるような場合大丈夫でしょうか?
あくまで検証するつもりはありませんが、各分割で切断点の除去さえしていけば点の数は減っていくと思うので
大丈夫だと思います。この場合の3、4分割でも30,50以上の点を除去するのは有効だと思います。

引用返信 編集キー/
■62957 / inTopicNo.29)  Re[25]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (76回)-(2011/11/13(Sun) 10:44:41)
問題は4分割目あたりですね。

2分割の真ん中を切断するタイムT2が10000という最大値で3分割の切断時間最小値がT3とします。

切断ポイントAiだけを考慮すると、3分割でタイムだけで検証すると5000*2500個程度の検証が必要になります。
shuさんの方法そのままだと分割場所の情報がないので切断したものを二人に分けられるかの検証がその後に必要になります

3分割の検証中で見つかった最小値でT3を更新しT3より大きな組み合わせは検証しないとしても組み合わせ数が大きなままである可能性は高いですね。

もし3分割で最小値T3が決まったとします。

4分割で何通りになるかです。
4分割でどの程度期待できるかはわかりませんが。

2500*1250*625=2億通り程度になるのではないでしょうか?
これに切断ポイントが正しく2等分しているかの検証を考慮に入れないといけません。

5分割だともっと計算はひどいことになりそうです。

もしかしたら最小タイムの収束が非常に早く実際はうまくいくのかもしれませんが、計算量の見積もりがとても困難で不安定なのではないでしょうか?
引用返信 編集キー/
■62960 / inTopicNo.30)  Re[26]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (77回)-(2011/11/13(Sun) 14:44:21)
2011/11/14(Mon) 13:33:35 編集(投稿者)

ダンジョンの問題難しいです。

まず回復地点の選定は回復回数も考慮に入れたナップザック問題になってるので、貪欲法で最も回復量の大きい所から使えるわけではない可能性が高い。

地下4階でHP0になり地下3階でHP最大値-まで2回で回復したことにして下り地下5階でもHP0になったので、地下3階はMax寸前まで回復したので意味なし今度は地下2階で回復していたことにすると地下3階で回復した分を無効化しないといけない。
この場合地下3階より上の階は無視すべきなのかもしれない?

地下10001階で大量回復できる地点があるなら地下10000階まではぎりぎりで進むなんて場合もある。
ある階で回復するかしないかは先の階や前の階を考慮しないといけない。

地下十万階あるのでナップザック法でも計算量が多くなる。
難しいです。

何かコロンブスの卵的発想一つなのかもしれません。
上記のように難しい問題なのですがどなたか良いアイディアをお願いします。

引用返信 編集キー/
■62963 / inTopicNo.31)  Re[26]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ shu (1083回)-(2011/11/14(Mon) 00:29:45)
2011/11/14(Mon) 08:56:46 編集(投稿者)

No62957 (堀江伸一 さん) に返信

> もしかしたら最小タイムの収束が非常に早く実際はうまくいくのかもしれませんが、計算量の見積もりがとても困難で不安定なのではないでしょうか?
前述してますが最適かどうか検証する気はないので参考程度だと思ってください。


#1問解決しているので解決済みで良いと思うのでチェック入れときます。これ以上続けるのは駄スレになるのでやめたほうが良いと
思います。
解決済み
引用返信 編集キー/
■62971 / inTopicNo.32)  Re[27]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (78回)-(2011/11/14(Mon) 12:14:23)
2011/11/14(Mon) 12:55:52 編集(投稿者)
2011/11/14(Mon) 12:20:39 編集(投稿者)
2011/11/14(Mon) 12:16:21 編集(投稿者)

そうですか?
次回のレスでshuさんの方法を再帰で実装して試して見ようかと考えていたのですが。
3分割でどの程度までタイムが縮まるかで計算量がけっこう変わりそうですしうまく行くなら面白いなと考えたのですが。

とりあえずダンジョンの問題は貪欲法で行けそうな気がしてきました。

地下1階から地下n階まで下った場合一番回復量の多い階mを覚えながら地下を降りていく。
HP0以下になったら覚えていた階で一度だけ回復していたことにする。(これは次の階で大量回復できる可能性を除外しないため)
最大HPが0を超えなかったら連続して回復する。
もしm階で回復したことにした結果m階時点でのHPが最大値を超えたら、m階での回復を加味した状態でm〜n階までの最も回復できる階をチェックしなおしその階にmを更新する。
これをHPが0以上になるまで繰り返す。
HPが0以上になったら次の階へ行きHPが0になったら同じことを繰り返す。

地下t階の回復量>=地下s階の回復量かつt>sのとき
地下t階で回復したことにした後地下s階で回復という手順は多分あり得ない。
sとtを比べた時地下T階での状態のほうがHPは減っているのは確実であるからtで回復したほうがよいしt階以後のHPの残存もt階で回復したほうがより多く残ることになる。

よってこの問題は貪欲法で解ける。
後はこの発想が正しいかどうかコードを書いて試してみるだけです。

この発想の問題はHPが一千万で始まり、地下1階で九百九十九万九千九百九十九減り、地下2階がHP2回復で近場の階がHP1回復の泉しかない構造を1セットとして、同じようなダメージと回復の構造が地下十万階まで何セットもあり各階でのダメージがそこそこの場合m〜n階までの更新チェック計算量が膨れ上がるという点です。

個人的には次の問題その次の問題で1000レス目までいきたかったのですが?
本当にスレ続けてはいけませんか?
引用返信 編集キー/
■62972 / inTopicNo.33)  Re[28]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 774RR (624回)-(2011/11/14(Mon) 12:39:42)
独り言ならば blog かなにかでやってほしいかなぁ。
少なくとも掲示板で続けても実入りが無いと思う。
引用返信 編集キー/
■62973 / inTopicNo.34)  Re[29]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (79回)-(2011/11/14(Mon) 12:56:43)
2011/11/14(Mon) 13:07:55 編集(投稿者)

そうですか。
どうしてもわからない問題だけ質問してますしそれにshuさんの返答があったのでそれで満足してますが?

読み返してください。
shuさんとは普通に会話になってるでしょ?
返答があったら普通に会話になるのです。

自分で自分にレスをするのは、問題の難点を返答する方に伝えて考えやすくしてもらうためであり、答えを書くのは自分で答えを考えついたのに返答を待つのは良くないと考えているからです。
引用返信 編集キー/
■62974 / inTopicNo.35)  Re[30]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 774RR (625回)-(2011/11/14(Mon) 13:06:40)
blog でも読者が付けばコメントが得られるであろうし、掲示板と状況は大差ないと思う。
ならば他人のリソースを間借りせずに自分のリソースで情報発信してみればいい。
掲示板の一般的読者より「特にその分野に特化した」読者がつく可能性は blog のほうが高いはず。

引用返信 編集キー/
■62975 / inTopicNo.36)  Re[31]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 堀江伸一 (80回)-(2011/11/14(Mon) 13:09:42)
うーん掲示板のリソースを使ってはいけないですか。
分からないことを質問するために掲示板があると考えていますが?
引用返信 編集キー/
■62977 / inTopicNo.37)  Re[32]: プログラムの練習問題をゆっくりと考えていきます
□投稿者/ 774RR (626回)-(2011/11/14(Mon) 14:00:08)
だれもダメとは言っていないが?ただ読者側の負担も考えてほしい。
この掲示板の表示形式で1000レスなどしようものなら読者側の負担が大きすぎる。
20件を超えると「次へ」を押さないと読めなくなってしまう。
興味の無い話題をわざわざボタン押しなおしてまで読み続けてくれる読者は少ないだろう。
読んでもらえなければ返信は減る(ないしは0になる)ことは理解できるよね。
少なくとも俺は20件越えた時点で複数の話題が乱立するこの記事を続けて読みたいとは思わない。

> 分からないことを質問するために掲示板があると考えていますが?
ならば「アルゴリズムに特化した掲示板」で続けるほうが実入りが多いと思うぞ。
そういう掲示板があるかどうかは知らないが。
引用返信 編集キー/

<前の20件
トピック内ページ移動 / << 0 | 1 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -