Чтобы привязываться к модели на обратной стороне, атрибуты name
элементов управления формы должны соответствовать свойствам модели. Использование цикла foreach
не создает правильные атрибуты имени. Если вы проверите html, вы увидите несколько экземпляров
<input type="text" name="item.LeaveType" .../>
, но для привязки к вашей модели элементы управления должны быть
<input type="text" name="LeaveDetailsList[0].LeaveType" .../>
<input type="text" name="LeaveDetailsList[1].LeaveType" .../>
и т. Д. Самый простой способ подумать об этом - рассмотреть вопрос о том, как вы получите доступ к значению свойства LeaveType
в коде C#
var model = new LeaveBalanceViewModel();
// add some LeaveBalanceDetails instances to the LeaveDetailsList property, then access a value
var leaveType = model.LeaveDetailsList[0].LeaveType;
. Поскольку ваш метод POST будет иметь имя параметра (скажем, model
), просто снимите префикс (model
), и именно так должен быть атрибут имени элемента управления. Для этого вы должны использовать либо цикл for
(сбор должен реализовывать IList<T>
)
for(int i = 0; i < Model.LeaveDetailsList.Count; i++)
{
@Html.TextBoxFor(m => m.LeaveDetailsList[i].LeaveType)
....
}
, либо использовать пользовательский EditorTemplate
(в коллекции необходимо реализовать только IEnumerable<T>
)
В /Views/Shared/EditorTemplates/LeaveBalanceDetails.cshtml
@model yourAssembly.LeaveBalanceDetails
<tr>
<td>@Html.TextBoxFor(m => m.LeaveType)</td>
....
</tr>
, а затем в основном представлении (не в цикле)
<table>
.... // add headings (preferably in a thead element
<tbody>
@Html.EditorFor(m => m.LeaveDetailsList)
</tbody>
</table>
и, наконец, в контроллере
public ActionResult Edit(LeaveBalanceViewModel model)
{
// iterate over model.LeaveDetailsList and save the items
}
Вы можете представить свой лабиринт как дерево.
A / \ / \ B C / \ / \ D E F G / \ \ H I J / \ L M / \ ** O (which could possibly represent) START + +---+---+ | A C G | +---+ + + + | D B | F | J | +---+---+ +---+---+ | L H E I | +---+ +---+---+ | M O | + +---+ FINISH (ignoring left-right ordering on the tree)
Где каждый узел является соединением путей. D, I, J, L и O - тупики, а ** - цель. Конечно, в вашем фактическом дереве каждый узел имеет возможность иметь до три детей.
Ваша цель теперь просто найти, какие узлы пересекаются, чтобы найти финиш , Любой алгоритм поиска дерева ol будет делать.
Глядя на дерево, довольно легко увидеть ваше правильное решение, просто «проследив» из ** в самой глубокой части дерева:
A B E H M **
Обратите внимание, что этот подход становится незначительно более сложным, когда у вас есть «петли» в вашем лабиринте (т. е. когда это возможно, без обратного слежения, вы снова вводите проход, который вы мы уже прошли).
Теперь давайте посмотрим на ваше первое решение, которое вы упомянули, применительно к этому дереву.
Ваше первое решение в основном - это Depth-First Поиск , что действительно не так уж плохо. На самом деле это довольно хороший рекурсивный поиск. В основном, в нем говорится: «Сначала используйте самый правый подход. Если ничего нет, отступите назад до первого места, вы можете пойти прямо или влево, а затем повторить.
Поиск по глубине будет искать выше дерево в этом порядке:
A B D (backtrack) E H L (backtrack) M ** (backtrack) O (backtrack thrice) I
(backtrack thrice) C F (backtrack) G J
Обратите внимание, что вы можете остановиться, как только найдете **.
Однако, когда вы на самом деле кодируете поиск по глубине, используя рекурсивный
Еще один способ поиска дерева - это .
Другой способ поиска дерева - это Breadth-First , который просматривает деревья по глубине. Он выполнил поиск по указанному выше дереву в следующем порядке:
A (next level) B C (next level) D E F G (next level)
H I J (next level) L M (next level) ** O
Обратите внимание, что из-за характера лабиринта ширина -начало имеет гораздо более высокое среднее количество узлов, которые он проверяет. Первый путь легко реализуется с помощью очереди путей для поиска, и каждая итерация, выбирая путь из очереди, «использует ding it ", получив все пути, через которые он может перейти после одного шага, и поместив эти новые пути в конец очереди. Нет никаких явных команд «следующего уровня» для кода, и они были там, чтобы помочь понять.
На самом деле существует целый расширенный список способов поиска дерева . Я просто упомянул два простых, самых простых способа.
Если ваш лабиринт очень, очень длинный и глубокий, имеет петли и сумасшествия, и это сложно, я предлагаю A * , который является стандартным алгоритмом поиска пути, который сочетает в себе поиск по методу «Ширина-Первый» с эвристикой ... вроде как «интеллектуальный поиск по ширине».
Он в основном работает следующим образом:
И это A *, который я представляю специально выделенным, потому что это более или менее стандартный алгоритм поиска путей для всех приложений для поиска путей , в том числе перемещение от одного края карты к другому, избегая внедорожных дорожек или гор и т. д. Это работает так хорошо, потому что использует кратчайшее расстояние эвристического расстояния , что дает ему «интеллект». A * настолько универсален, потому что, учитывая любую проблему, если у вас есть минимально возможная эвристика расстояния (наша простая - прямая линия), вы можете применить ее.
НО, но это имеет большое значение для обратите внимание, что A * - , а не ваш единственный вариант.
Фактически, в категория википедии алгоритмов обхода дерева перечислены только 97! (лучшее будет по-прежнему находиться на этой странице , связанное ранее)
Извините за длину = P (я имею тенденцию блуждать)
Просто идея. Почему бы не бросить туда некоторых ботов в стиле monte carlo. Назовем первое поколение ботов gen0. Мы сохраняем ботов от gen0, у которых есть некоторые непрерывные дороги таким образом: от начала до некоторой точки или от некоторой точки до конца
Мы запускаем новый gen1 ботов в новых случайных точках, то мы пытаемся соединить дороги ботов gen1 с gen0 и посмотреть, получим ли мы непрерывную дорогу от начала до конца.
Итак, для genn мы пытаемся соединиться с формой ботов gen0, gen1 , ..., genn-1.
Конечно, поколение длится всего лишь конечное количество времени.
Я не знаю, будет ли цвет лица алгоритма быть практичным для небольших наборов данных. Также алгоритм предполагает, что мы знаем начальные и конечные точки.
некоторые хорошие сайты для идей: http://citeseerx.ist.psu.edu/ http: // arxiv.org/
Интересный подход, по крайней мере, мне показалось интересным, - использовать клеточные автоматы. Короче говоря, «космическая» ячейка, окруженная 3-дюймовыми «ячейками», превращается в «стенную» ячейку. В конце остаются только оставшиеся ячейки, оставшиеся на пути к выходу.
Если вы посмотрите на дерево, которое Джастин положил в свой ответ, вы увидите, что листовые узлы имеют 3 стены. Обрезайте дерево до тех пор, пока у вас не будет пути.
существует множество алгоритмов, и многие разные настройки , которые определяют, какой алгоритм лучше всего. это всего лишь одна идея об интересной настройке:
предположим, что у вас есть следующие свойства ...
, тогда вы можете спроектировать AI который ...
Не специально для вашего случая, но я столкнулся с несколькими вопросами программирования, где я нашел алгоритм Ли весьма удобным для быстрого кода. Он не самый эффективный для всех случаев, но легко вывернуться. Вот one Я взломал для конкурса.
У меня была аналогичная проблема в одном из моих университетских Comp. Sci. курсы. Решение, которое мы придумали, состояло в том, чтобы следовать за левой стенкой (правая стенка будет работать так же хорошо). Вот какой-то псевдокод
While Not At End
If Square To Left is open,
Rotate Left
Go Forward
Else
Rotate Right
End If
Wend
Это в основном это. Сложная часть отслеживает, в каком направлении ваша сторона, и выяснить, какая позиция сетки находится слева от вас в зависимости от этого направления. Это сработало для любого тестового примера, с которым я столкнулся. Довольно интересно, что решение «Профессора» было чем-то вроде:
While Not At End
If Can Go North
Go North
ElseIf Can Go East
Go East
ElseIf Can Go South
Go South
ElseIf Can Go West
Go West
EndIf
Wend
. Это будет хорошо работать для большинства простых лабиринтов, но не работает в лабиринте, который выглядит следующим образом:
SXXXXXXXXXXXXX
X X
X X
X X
XXX X
X X X
X XXXXXXXXXXX XXXE
X X
XXXXXXXXXXXXXXXXXXX
Когда S и E являются началом и концом.
С чем-либо, что не следует за стеной, вам нужно сохранить список мест, где вы были, чтобы вы могли вернуться в случае необходимости, когда вы впадаете в тупик, и поэтому что вы не попадаете в петлю. Если вы будете следовать за стеной, вам не нужно отслеживать, где вы были. Хотя вы не найдете наиболее оптимальный путь через лабиринт, вы всегда сможете пройти через него.
Как насчет построения графика из вашей Матрицы и использования первого поиска Breadth, первого поиска или алгоритма Дейкстры?
Это один из моих любимых алгоритмов когда-либо ....
1) Move forward
2) Are you at a wall?
2a) If yes, turn left
3) Are you at the finish?
3a) If no, go to 1
3b) If yes, solved
Если робот может отслеживать свое местоположение, поэтому он знает, было ли это место раньше, то поиск по глубине - это очевидный алгоритм. Вы можете показать аргумент состязательности, что невозможно добиться лучшей производительности в худшем случае, чем поиск по глубине.
Если у вас есть доступные вам методы, которые не могут быть реализованы роботами, поиск может работать лучше для многих лабиринтов, а также алгоритм Дейкстры для нахождения кратчайшего пути на графике.
Этот алгоритм azkaban также может помочь вам, http://journals.analysisofalgorithms.com/2011/08/efficient-maze-solving-approach-with.html
Тот же ответ, что и все вопросы о переполнении стека;)
Используйте vi!
http://www.texteditors.org/cgi-bin/wiki. pl? Vi-Maze
Поистине увлекательно видеть, как текстовый редактор решает ascii-лабиринт, я уверен, что у парней emacs есть эквивалент ..
Лучшим способом решения лабиринта является использование алгоритма подключения, такого как union-find, который является квазилинейным алгоритмом времени, предполагающим сжатие пути.
Union-Find - это структура данных, которая говорит вам, что транзисторно соединены два элемента в наборе.
Чтобы использовать структуру данных поиска соединения для решения лабиринта, сначала данные о соседних связях используются для построения структуры данных объединения. Тогда нахождение объединения сжимается. Чтобы определить, разрешен ли лабиринт, сравниваются входные и выходные значения. Если они имеют одинаковое значение, то они связаны и лабиринт разрешима. Наконец, чтобы найти решение, вы начинаете с входа и исследуете корень, связанный с каждым из его соседей. Как только вы обнаружите ранее невидимого соседа с тем же корнем, что и текущая ячейка, вы заходите в эту ячейку и повторяете процесс.
Основным недостатком этого подхода является то, что он не укажет вам кратчайший маршрут через лабиринт, если есть более одного пути.
Это очень простое представление для моделирования лабиринта в C ++:)
#ifndef vAlgorithms_Interview_graph_maze_better_h
#define vAlgorithms_Interview_graph_maze_better_h
static const int kMaxRows = 100;
static const int kMaxColumns = 100;
class MazeSolver
{
private:
char m_matrix[kMaxRows][kMaxColumns]; //matrix representation of graph
int rows, cols; //actual rows and columns
bool m_exit_found;
int m_exit_row, m_exit_col;
int m_entrance_row, m_entrance_col;
struct square //abstraction for data stored in every verex
{
pair<int, int> m_coord; //x and y co-ordinates of the matrix
square* m_parent; //to trace the path backwards
square() : m_parent(0) {}
};
queue<square*> Q;
public:
MazeSolver(const char* filename)
: m_exit_found(false)
, m_exit_row(0)
, m_exit_col(0)
, m_entrance_row(0)
, m_entrance_col(0)
{
ifstream file;
file.open(filename);
if(!file)
{
cout << "could not open the file" << endl << flush;
// in real world, put this in second phase constructor
}
init_matrix(file);
}
~MazeSolver()
{
}
void solve_maze()
{
//we will basically use BFS: keep pushing squares on q, visit all 4 neighbors and see
//which way can we proceed depending on obstacle(wall)
square* s = new square();
s->m_coord = make_pair(m_entrance_row, m_entrance_col);
Q.push(s);
while(!m_exit_found && !Q.empty())
{
s = Q.front();
Q.pop();
int x = s->m_coord.first;
int y = s->m_coord.second;
//check if this square is an exit cell
if(x == m_exit_row && y == m_exit_col)
{
m_matrix[x][y] = '>'; // end of the path
m_exit_found = true;
//todo: try breaking? no= queue wont empty
}
else
{
//try walking all 4 neighbors and select best path
//NOTE: Since we check all 4 neighbors simultaneously,
// the path will be the shortest path
walk_path(x-1, y, s);
walk_path(x+1, y, s);
walk_path(x, y-1, s);
walk_path(x, y+1, s);
}
} /* end while */
clear_maze(); //unset all previously marked visited shit
//put the traversed path in maze for printing
while(s->m_parent)
{
m_matrix[s->m_coord.first][s->m_coord.second] = '-';
s = s->m_parent;
} /* end while */
}
void print()
{
for(int i=0; i<rows; i++)
{
for(int j=0; j<cols; j++)
cout << m_matrix[i][j];
cout << endl << flush;
}
}
private:
void init_matrix(ifstream& file)
{
//read the contents line-wise
string line;
int row=0;
while(!file.eof())
{
std::getline(file, line);
for(int i=0; i<line.size(); i++)
{
m_matrix[row][i] = line[i];
}
row++;
if(line.size() > 0)
{
cols = line.size();
}
} /* end while */
rows = row - 1;
find_exit_and_entry();
m_exit_found = false;
}
//find and mark ramp and exit points
void find_exit_and_entry()
{
for(int i=0; i<rows; i++)
{
if(m_matrix[i][cols-1] == ' ')
{
m_exit_row = i;
m_exit_col = cols - 1;
}
if(m_matrix[i][0] == ' ')
{
m_entrance_row = i;
m_entrance_col = 0;
}
} /* end for */
//mark entry and exit for testing
m_matrix[m_entrance_row][m_entrance_col] = 's';
m_matrix[m_exit_row][m_exit_col] = 'e';
}
void clear_maze()
{
for(int x=0; x<rows; x++)
for(int y=0; y<cols; y++)
if(m_matrix[x][y] == '-')
m_matrix[x][y] = ' ';
}
// Take a square, see if it's the exit. If not,
// push it onto the queue so its (possible) pathways
// are checked.
void walk_path(int x, int y, square* parent)
{
if(m_exit_found) return;
if(x==m_exit_row && y==m_exit_col)
{
m_matrix[x][y] = '>';
m_exit_found = true;
}
else
{
if(can_walk_at(x, y))
{
//tag this cell as visited
m_matrix[x][y] = '-';
cout << "can walk = " << x << ", " << y << endl << flush;
//add to queue
square* s = new square();
s->m_parent = parent;
s->m_coord = make_pair(x, y);
Q.push(s);
}
}
}
bool can_walk_at(int x, int y)
{
bool oob = is_out_of_bounds(x, y);
bool visited = m_matrix[x][y] == '-';
bool walled = m_matrix[x][y] == '#';
return ( !oob && !visited && !walled);
}
bool is_out_of_bounds(int x, int y)
{
if(x<0 || x > rows || y<0 || y>cols)
return true;
return false;
}
};
void run_test_graph_maze_better()
{
MazeSolver m("/Users/vshakya/Dropbox/private/graph/maze.txt");
m.print();
m.solve_maze();
m.print();
}
#endif
Существует множество алгоритмов решения лабиринта:
http://en.wikipedia.org/wiki/Maze_solving_algorithm
http: //www.astrolog.org/labyrnth/algrithm.htm#solve
Для робота алгоритм Тремо [] выглядит многообещающим.