|
Hey there.
So as my final in algorithms is getting closer, I'm trying to scrutinize some DP problems from past tests.
Input: A list L of strings, and another string s.
Output: "yes" if s is a concatenation of string from L, "no" otherwise.
Example: AGAGADIR is a concatenation of strings from the list {ADI, AG, R, SI}; but the string SIRA is not. Assume that checking if a string is contained in L costs O(1). Also, notice the option of an empty string.
My way of thinking about DP solution: The aim is initially to define the right sub-problems of the main problem, and then to develop the right recursion with the suitable memoization.
Sub-problems: we'll look at the string s up to the char at index i (between 0 and Length(s) - 1) and decide if it's itself a concatenation of strings from L.
Recursion: if i == 0 then the empty list is trivially in L, continue to i = 1; if i == 1 then go over the list L and compare if there is a char element in L which equals to the first character of s - if not, check for i=2, otherwise... here I'm stuck
Can someone please direct me? Give me some sense for DP?
Thanks in advance.
|
|
|
|
|
Member 14873814 wrote: DP problems
What is a DP problem ?
What i in your question ?
Patrice
“Everything should be made as simple as possible, but no simpler.” Albert Einstein
|
|
|
|
|
Member 14873814 wrote: Example: AGAGADIR is a concatenation of strings from the list {ADI, AG, R, SI}; Where is "SI" in that? But "AGAGADIR" could also be considered to contain the strings "AG", "AGA", "GA", "GAD", "GADI" etc. You have to decide how you delineate each part that makes the whole string.
|
|
|
|
|
List<string> L = new List<string>() { "ADI", "AG", "R", "SI" };
StringBuilder sb = new StringBuilder( "AGAGADIR" );
foreach ( string l in L ) { sb.Replace( l, "" ); }
Console.WriteLine( sb.Length == 0 ? "YES" : "NO" );
sb = new StringBuilder( "SIRA" );
foreach ( string l in L ) { sb.Replace( l, "" ); }
Console.WriteLine( sb.Length == 0 ? "YES" : "NO" );
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
A recursive solution would be simple, something like:
bool CanBeConstructedFromPartsList(string word) {
if (word is empty) return true
result=false
foreach(part in partslist) {
if (word starts with part) result |= CanBeConstructedFromPartsList(word with part removed)
}
return result
}
I don't see a way to do better by storing some intermediate results though.
|
|
|
|
|
What is being done in following algorithm?
dmin ← ∞
for i ← 0 to n − 1 do
for j ← 0 to n − 1 do
if i != j and |A[i] − A[j]| < dmin
dmin ← |A[i] − A[j]|
return dmin
|
|
|
|
|
This smells of a homework assignment or a take-home test.
Walk through the code by hand, step-by-step, and start writing values down. You might find it pretty easy to figure out what it's doing.
|
|
|
|
|
It is not doing anything, since n and A are not defined.
|
|
|
|
|
Hey there.
First, it's not my homework, I've done my semester and I'm studying for my finals.
I find the course of Algorithms very difficult for me, and I would like to go over some exercises from past finals.
Here is one:
Given an array of jobs with different time requirements. There are K identical assignees available. Suggest an algorithm that, given K identical assignees, time-requirement for n jobs (t1, ..., tn), and time T1 answers "Yes" if it's possible to split the jobs among the assignees such that the maximum time required for each assignee to accomplish his jobs is at most T1. Otherwise, answer "No".
The following constraints:
1. An assignee can be assigned only contiguous jobs. For example, an assignee cannot be assigned jobs 1 and 3, but not 2.
2. Two assignees cannot share (or co-assigned) a job, i.e., a job cannot be partially assigned to one assignee and partially to other
I thought of the greedy way - going over the job times, and sum them up to each assignee as far as possible. Also, I don't know if some sorting can help to make it more efficient.
Dynamic programming way - can't even start thinking about solution using DP. We are told to observe sub-problems of our problem etc... But I don't even see some suitable sub-problem. Can someone direct me please? How do you start thinking about solution using DP?
Thanks in advance.
|
|
|
|
|
From the dark days of my project management life I think this is what we used to call CPA: Critical Path Analysis. So you need to build a picture of each stream where jobs must run in sequence, and how long they take. You can then figure out how many can actually run, given the number of assignees available. I have only ever done this manually by drawing a diagram on a blackboard, but I know there are plenty of packages around to help (even Microsoft Project). So start with some paper diagrams to get your thoughts clear on how to approach it in terms of logical steps.
BTW, the statement "an assignee cannot be assigned jobs 1 and 3, but not 2." does not make sense, as both parts are negative.
|
|
|
|
|
Actually, that doesn't help me to reach to something interesting...
I look at that this way: iterate over the jobs, summing their time-requirement up, and each time I sum another one up - I check if an assignee is able to... Actually, I don't know even what my condition has to be at all
Frustrating...
|
|
|
|
|
Sorry, that is the best I can offer, it's been more than 30 years.
|
|
|
|
|
I've never worked on this problem. Coming up with a solution is one thing, but coming up with the optimal solution is quite another. It seems to resemble the bin packing problem[^], which is NP-complete, but it has the additional constraint of having to assign contiguous jobs.
|
|
|
|
|
Hey, I've edited the problem.
It seems be different now from the bin one.
|
|
|
|
|
You need to provide an accurate description of the problem the first time so that people don't waste their time.
|
|
|
|
|
Checking whether the time limit of T1 can be met is easier than optimizing from scratch. You don't need DP, you don't need sorting, your greedy solution already does the job. It only takes linear time and there is nothing better you could do in that sense: the input all needs to be seen anyway so anything will take at least linear time.
|
|
|
|
|
Okay.
How can I know at all if my algorithm is "good enough" and "does the work"?
Another way of asking my question: how does one think of the correctness proof? The optimality and lawfulness?
Thank you!
The next section of the problem asks us for the following:
Use the first algorithm (...at most T1...) to describe a polynomial algorithm calculating the optimal time T to accomplish all the jobs.
What's the approach?
**Greg Utas**: You're right - won't happen again.
modified 26-Jun-20 7:52am.
|
|
|
|
|
Search for the lowest T1 such that the previous algorithm returns True.
For example, that can be done in O(log(S)) where S is the Sum of all times, doing it in O(S) would not be polynomial time because S can be exponential in the size of the input (imagine if the times are very few but very large integers), but log(S) is fine.
|
|
|
|
|
Hmm so you actually direct me to some approach where I have to only divide each time by 2.
The logic behind that, I think, is:
If at first we see that we can split our jobs (Total time = S) such that T1 = S (That is, K equals at least to 1), then let's find out if we can split it to 2 assignees equally - now we'll send to our first algorithm as a parameter: 0.5*S.
So we divide by 2 and send to the first algorithm, so on...
Am I right?
BTW, how do I think about proving the optimality of such an algprithm?
Thank you!
|
|
|
|
|
You can't choose K, right?
A useful propery of Algorithm1 is that:
- if Algorithm1(T1) is false, then Algorithm1(T1-k) is also false
- if Algorithm1(T1) is true, then Algorithm1(T1+k) is also true
Or in other words, there is a threshold below which it returns false (not enough time) and above which it returns true (enough time).
|
|
|
|
|
I agree obviously that this property indeed holds, but... I can't see how I should use it in some manner of dividing each time the total time by some constant (log(S)).
Maybe you can give me another clue? Some approach?
Thank you!
|
|
|
|
|
If T1 was chosen in the middle of a range, then whichever result Algorithm1(T1) gives, half of the range will be uninteresting (either all False or all True).
|
|
|
|
|
@harold aptroot -
Sorry, but I'm not sure I got your point.
I agree if you mean the following: In case we've divided S by 2, and Algorithm1(T1) == TRUE, then we know for sure that T_Optimal <= T1.
So the algorithm should go as I described before, no? With the division each time by 2. But it's probably not sufficient as we may skip on some T1 which gives FALSE, and from there we don't know the amount of time units to add up...
|
|
|
|
|
When the result for T1 is False, that also cuts off half of the current range: the first half.
|
|
|
|
|
Genius. It reminds me a little bit the procedure of Partition in QuickSort.
So we sort with Algorithm1(S), if it returns TRUE we call Algorithm(0.5 * S), and so on...
If at some point (after i steps) we call Algorithm1(0.5^i * S), and it returns FALSE, we call Algorithm1(0.5 * (0.5^i * S + 0.5^(i-1) * S)), and so on...
Hmmm actually now I'm not sure about my suggestion.
Should I actually initialize an array of size S with all the natural numbers from 1 to S?
Thank you!
modified 26-Jun-20 16:56pm.
|
|
|
|