トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
8-1 ナップサック問題
問題
ソース(深さ優先探索)
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
struct ItemInfoDef
{
internal char ID;
internal int Omosa;
internal int Val;
}
static List<ItemInfoDef> ItemList = new List<ItemInfoDef>();
struct JyoutaiDef
{
internal int CurrP;
internal int OmosaSum;
internal int SumVal;
internal List<ItemInfoDef> SelectedItemList;
}
//重さ合計の制限
const int OmosaSumLimit = 10;
static void Main()
{
ItemList.Add(new ItemInfoDef() { ID = 'A', Omosa = 3, Val = 2 });
ItemList.Add(new ItemInfoDef() { ID = 'B', Omosa = 4, Val = 3 });
ItemList.Add(new ItemInfoDef() { ID = 'C', Omosa = 1, Val = 2 });
ItemList.Add(new ItemInfoDef() { ID = 'D', Omosa = 2, Val = 3 });
ItemList.Add(new ItemInfoDef() { ID = 'E', Omosa = 3, Val = 6 });
var JyoutaiDefList = new List<JyoutaiDef>();
var stk = new Stack<JyoutaiDef>();
for (int I = 0; I <= ItemList.Count - 1; I++) {
JyoutaiDef WillPush;
WillPush.CurrP = I;
WillPush.OmosaSum = ItemList[I].Omosa;
WillPush.SumVal = ItemList[I].Val;
WillPush.SelectedItemList = new List<ItemInfoDef>() { ItemList[I] };
stk.Push(WillPush);
}
while (stk.Count > 0) {
JyoutaiDef Popped = stk.Pop();
JyoutaiDefList.Add(Popped);
for (int I = Popped.CurrP + 1; I <= ItemList.Count - 1; I++) {
JyoutaiDef WillPush;
WillPush.CurrP = I;
WillPush.OmosaSum = Popped.OmosaSum + ItemList[I].Omosa;
WillPush.SumVal = Popped.SumVal + ItemList[I].Val;
WillPush.SelectedItemList = new List<ItemInfoDef>(Popped.SelectedItemList);
WillPush.SelectedItemList.Add(ItemList[I]);
if (WillPush.OmosaSum <= OmosaSumLimit) stk.Push(WillPush);
}
}
int MaxSumVal = JyoutaiDefList.Max(X => X.SumVal);
JyoutaiDef AnswerJyoutai = JyoutaiDefList.First(X => X.SumVal == MaxSumVal);
Console.WriteLine("深さ優先探索の結果を表示します。");
Console.WriteLine("重さ合計が{0}以下での最大の価値は、", OmosaSumLimit);
Console.WriteLine("重さ合計={0}で価値合計={1}です。", AnswerJyoutai.OmosaSum, MaxSumVal);
Console.WriteLine();
Console.WriteLine(new string('■', 30));
Console.WriteLine("選択した荷物の情報を表示します。");
foreach (ItemInfoDef EachItem in AnswerJyoutai.SelectedItemList) {
Console.WriteLine("ID={0}、重さ={1}、価値={2}",
EachItem.ID, EachItem.Omosa, EachItem.Val);
}
}
}
実行結果(深さ優先探索)
重さ合計が10以下での最大の価値は、
重さ合計=10で価値合計=14です。
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
選択した荷物の情報を表示します。
ID=B、重さ=4、価値=3
ID=C、重さ=1、価値=2
ID=D、重さ=2、価値=3
ID=E、重さ=3、価値=6
ソース(動的計画法)
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
struct ItemInfoDef
{
internal char ID;
internal int Omosa;
internal int Val;
}
static List<ItemInfoDef> ItemList = new List<ItemInfoDef>();
//重さ合計の制限
const int OmosaSumLimit = 10;
//価値合計の最大値[重さの合計]のDP表
static Nullable<int>[] DPArr = new Nullable<int>[OmosaSumLimit + 1];
//最後に矢印をセットした品物の名称
static char[] LastArrowItemArr = Enumerable.Repeat(' ', OmosaSumLimit + 1).ToArray();
static List<char[]> LastArrowItemArrLog = new List<char[]>();
static void Main()
{
ItemList.Add(new ItemInfoDef() { ID = 'A', Omosa = 3, Val = 2 });
ItemList.Add(new ItemInfoDef() { ID = 'B', Omosa = 4, Val = 3 });
ItemList.Add(new ItemInfoDef() { ID = 'C', Omosa = 1, Val = 2 });
ItemList.Add(new ItemInfoDef() { ID = 'D', Omosa = 2, Val = 3 });
ItemList.Add(new ItemInfoDef() { ID = 'E', Omosa = 3, Val = 6 });
//初期設定
DPArr[0] = 0;
foreach (ItemInfoDef EachItem in ItemList) {
for (int I = OmosaSumLimit; 0 <= I; I--) {
//重さ制限を超えてしまう場合
int wkSumOmosa = I + EachItem.Omosa;
if (wkSumOmosa > OmosaSumLimit) continue;
//価値の和を求める
if (DPArr[I] == null) continue;
int wkSumVal = DPArr[I].Value + EachItem.Val;
//重さ合計での最大の価値を更新可能かを判定
if (DPArr[wkSumOmosa] == null
|| DPArr[wkSumOmosa] < wkSumVal) {
DPArr[wkSumOmosa] = wkSumVal;
LastArrowItemArr[wkSumOmosa] = EachItem.ID;
}
}
LastArrowItemArrLog.Add((char[])LastArrowItemArr.Clone());
Console.WriteLine(new string('■', 30));
Console.WriteLine("{0}の処理結果", EachItem.ID);
PrintJyoutai();
}
Console.WriteLine(new string('■', 30));
Console.WriteLine("動的計画法の結果を表示します。");
PrintDP();
}
//動的計画法の現在の状態を表示
static void PrintJyoutai()
{
var sb1 = new System.Text.StringBuilder();
var sb2 = new System.Text.StringBuilder();
for (int I = 0; I <= DPArr.GetUpperBound(0); I++) {
sb1.AppendFormat("[{0,2}]", I);
sb2.AppendFormat(" {0,2} ", DPArr[I]);
}
Console.WriteLine(sb1.ToString());
Console.WriteLine(sb2.ToString());
Console.WriteLine();
sb1.Length = sb2.Length = 0;
for (int I = 0; I <= LastArrowItemArr.GetUpperBound(0); I++) {
sb1.AppendFormat("[{0,2}]", I);
sb2.AppendFormat(" {0,2} ", LastArrowItemArr[I]);
}
Console.WriteLine(sb1.ToString());
Console.WriteLine(sb2.ToString());
}
//動的計画法の結果を表示
static void PrintDP()
{
//最大の価値を持つ、重さ合計を求める
int MaxSumVal = DPArr.Max(X => X ?? 0);
int MaxSumInd = Array.FindIndex(DPArr, X => X == MaxSumVal);
Console.WriteLine("重さ合計が{0}以下での最大の価値は、", OmosaSumLimit);
Console.WriteLine("重さ合計={0}で価値合計={1}です。", MaxSumInd, MaxSumVal);
Console.WriteLine();
Console.WriteLine(new string('■', 30));
Console.WriteLine("選択した荷物の情報を表示します。");
int CurrInd = MaxSumInd;
var SelectedItemList = new List<ItemInfoDef>();
for (int I = LastArrowItemArrLog.Count - 1; 0 <= I; I--) {
char[] CurrLog = LastArrowItemArrLog[I];
//使用済の荷物を含むログならContinue
if (CurrLog.Intersect(SelectedItemList.Select(X => X.ID)).Any()) {
continue;
}
ItemInfoDef CurrArrowItem = ItemList.Single(X => X.ID == CurrLog[CurrInd]);
SelectedItemList.Add(CurrArrowItem);
CurrInd -= CurrArrowItem.Omosa;
if (CurrInd == 0) break;
}
foreach (ItemInfoDef EachItem in SelectedItemList) {
Console.WriteLine("ID={0}、重さ={1}、価値={2}",
EachItem.ID, EachItem.Omosa, EachItem.Val);
}
}
}
実行結果(動的計画法)
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Aの処理結果
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
0 2
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
A
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Bの処理結果
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
0 2 3 5
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
A B B
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Cの処理結果
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
0 2 2 4 5 5 7
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
C A C C B C
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Dの処理結果
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
0 2 3 5 4 5 7 8 7 8 10
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
C D D C C D D C D D
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Eの処理結果
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
0 2 3 6 8 9 11 10 11 13 14
[ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7][ 8][ 9][10]
C D E E E E E E E E
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
動的計画法の結果を表示します。
重さ合計が10以下での最大の価値は、
重さ合計=10で価値合計=14です。
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
選択した荷物の情報を表示します。
ID=E、重さ=3、価値=6
ID=D、重さ=2、価値=3
ID=C、重さ=1、価値=2
ID=B、重さ=4、価値=3
解説
深さ優先探索よりも、動的計画法で解いたほうが、計算量は、少ないです。