Tuesday, March 19, 2013

Bitwise Operators in C and C++: A Tutorial


http://www.cprogramming.com/tutorial/bitwise_operators.html

Bitwise Operators in C and C++: A Tutorial

Generally, as a programmer you don't need to concern yourself about operations at the bit level. You're free to think in bytes, or ints and doubles, or even higher level data types composed of a combination of these. But there are times when you'd like to be able to go to the level of an individual bit. Exclusive-or encryption is one example when you need bitwise operations. 



Another example comes up when dealing with data compression: what if you wanted to compress a file? In principle, this means taking one representation and turning it into a representation that takes less space. One way of doing this is to use an encoding that takes less than 8 bits to store a byte. (For instance, if you knew that you would only be using the 26 letters of the Roman alphabet and didn't care about capitalization, you'd only need 5 bits to do it.) In order to encode and decode files compressed in this manner, you need to actually extract data at the bit level. 

Finally, you can use bit operations to speed up your program or perform neat tricks. (This isn't always the best thing to do.)

Thinking about Bits

The byte is the lowest level at which we can access data; there's no "bit" type, and we can't ask for an individual bit. In fact, we can't even perform operations on a single bit -- every bitwise operator will be applied to, at a minimum, an entire byte at a time. This means we'll be considering the whole representation of a number whenever we talk about applying a bitwise operator. (Note that this doesn't mean we can't ever change only one bit at a time; it just means we have to be smart about how we do it.) Understanding what it means to apply a bitwise operator to an entire string of bits is probably easiest to see with the shifting operators. By convention, in C and C++ you can think about binary numbers as starting with the most significant bit to the left (i.e., 10000000 is 128, and 00000001 is 1). Regardless of underlying representation, you may treat this as true. As a consequence, the results of the left and right shift operators are not implementation dependent for unsigned numbers (for signed numbers, the right shift operator is implementation defined). 

The leftshift operator is the equivalent of moving all the bits of a number a specified number of places to the left:
[variable]<<[number of places]
For instance, consider the number 8 written in binary 00001000. If we wanted to shift it to the left 2 places, we'd end up with 00100000; everything is moved to the left two places, and zeros are added as padding. This is the number 32 -- in fact, left shifting is the equivalent of multiplying by a power of two.
int mult_by_pow_2(int number, int power)
{
    return number<<power;
}
Note that in this example, we're using integers, which are either 2 or 4 bytes, and that the operation gets applied to the entire sequence of 16 or 32 bits. 

But what happens if we shift a number like 128 and we're only storing it in a single byte: 10000000? Well, 128 * 2 = 256, and we can't even store a number that big in a byte, so it shouldn't be surprising that the result is 00000000. 

It shouldn't surprise you that there's a corresponding right-shift operator: >> (especially considering that I mentioned it earlier). Note that a bitwise right-shift will be the equivalent of integer division by 2. 

Why is it integer division? Consider the number 5, in binary, 00000101. 5/2 is 2.5, but if you are performing integer division, 5/2 is 2. When you perform a right shift by one: (unsigned int)5>>1, you end up with 00000010, as the rightmost 1 gets shifted off the end; this is the representation of the number 2. Note that this only holds true for unsigned integers; otherwise, we are not guaranteed that the padding bits will be all 0s. 

Generally, using the left and right shift operators will result in significantly faster code than calculating and then multiplying by a power of two. The shift operators will also be useful later when we look at how to manipulating individual bits. 

For now, let's look at some of the other binary operators to see what they can do for us.

Bitwise AND

The bitwise AND operator is a single ampersand: &. A handy mnemonic is that the small version of the boolean AND, &&, works on smaller pieces (bits instead of bytes, chars, integers, etc). In essence, a binary AND simply takes the logical AND of the bits in each position of a number in binary form. 

For instance, working with a byte (the char type):
01001000 & 
10111000 = 
--------
00001000
The most significant bit of the first number is 0, so we know the most significant bit of the result must be 0; in the second most significant bit, the bit of second number is zero, so we have the same result. The only time where both bits are 1, which is the only time the result will be 1, is the fifth bit from the left. Consequently,
72 & 184 = 8

Bitwise OR

Bitwise OR works almost exactly the same way as bitwise AND. The only difference is that only one of the two bits needs to be a 1 for that position's bit in the result to be 1. (If both bits are a 1, the result will also have a 1 in that position.) The symbol is a pipe: |. Again, this is similar to boolean logical operator, which is ||.
01001000 | 
10111000 = 
--------
11111000
and consequently
72 | 184 = 248
Let's take a look at an example of when you could use just these four operators to do something potentially useful. Let's say that you wanted to keep track of certain boolean attributes about something -- for instance, you might have eight cars (!) and want to keep track of which are in use. Let's assign each of the cars a number from 0 to 7. 

Since we have eight items, all we really need is a single byte, and we'll use each of its eight bits to indicate whether or not a car is in use. To do this, we'll declare a char called in_use, and set it to zero. (We'll assume that none of the cars are initially "in use".)
char in_use = 0;
Now, how can we check to make sure that a particular car is free before we try to use it? Well, we need to isolate the one bit that corresponds to that car. The strategy is simple: use bitwise operators to ensure every bit of the result is zero except, possibly, for the bit we want to extract. 

Consider trying to extract the fifth bit from the right of a number: XX?XXXXX We want to know what the question mark is, and we aren't concerned about the Xs. We'd like to be sure that the X bits don't interfere with our result, so we probably need to use a bitwise AND of some kind to make sure they are all zeros. What about the question mark? If it's a 1, and we take the bitwise AND of XX?XXXXX and 00100000, then the result will be 00100000:
XX1XXXXX & 
00100000 = 
--------
00100000
Whereas, if it's a zero, then the result will be 00000000:
XX0XXXXX & 
00100000 = 
--------
00000000
So we get a non-zero number if, and only if, the bit we're interested in is a 1. 

This procedure works for finding the bit in the nth position. The only thing left to do is to create a number with only the one bit in the correct position turned on. These are just powers of two, so one approach might be to do something like:
int is_in_use(int car_num)
{
    // pow returns an int, but in_use will also be promoted to an int
    // so it doesn't have any effect; we can think of this as an operation
    // between chars
    return in_use & pow(2, car_num);
}
While this function works, it can be confusing. It obscures the fact that what we want to do is shift a bit over a certain number of places, so that we have a number like 00100000 -- a couple of zeros, a one, and some more zeros. (The one could also be first or last -- 10000000 or 00000001.) 

We can use a bitwise leftshift to accomplish this, and it'll be much faster to boot. If we start with the number 1, we are guaranteed to have only a single bit, and we know it's to the far-right. We'll keep in mind that car 0 will have its data stored in the rightmost bit, and car 7 will be the leftmost.
int is_in_use(int car_num)
{
    return in_use & 1<<car_num;
}
Note that shifting by zero places is a legal operation -- we'll just get back the same number we started with. 

All we can do right now is check whether a car is in use; we can't actually set the in-use bit for it. There are two cases to consider: indicating a car is in use, and removing a car from use. In one case, we need to turn a bit on, and in the other, turn a bit off. 

Let's tackle the problem of turning the bit on. What does this suggest we should do? If we have a bit set to zero, the only way we know right now to set it to 1 is to do a bitwise OR. Conveniently, if we perform a bitwise OR with only a single bit set to 1 (the rest are 0), then we won't affect the rest of the number because anything ORed with zero remains the same (1 OR 0 is 1, and 0 OR 0 is 0). 

Again we need to move a single bit into the correct position:
void set_in_use(int car_num)
{
    in_use = in_use | 1<<car_num;
}
What does this do? Take the case of setting the rightmost bit to 1: we have some number 0XXXXXXX | 10000000; the result, 1XXXXXXX. The shift is the same as before; the only difference is the operator and that we store the result. 

Setting a car to be no longer in use is a bit more complicated. For that, we'll need another operator.

The Bitwise Complement

The bitwise complement operator, the tilde, ~, flips every bit. A useful way to remember this is that the tilde is sometimes called a twiddle, and the bitwise complement twiddles every bit: if you have a 1, it's a 0, and if you have a 0, it's a 1. 

This turns out to be a great way of finding the largest possible value for an unsigned number:
unsigned int max = ~0;
0, of course, is all 0s: 00000000 00000000. Once we twiddle 0, we get all 1s: 11111111 11111111. Since max is an unsigned int, we don't have to worry about sign bits or twos complement. We know that all 1s is the largest possible number. 

Note that ~ and ! cannot be used interchangeably. When you take the logical NOT of a non-zero number, you get 0 (FALSE). However, when you twiddle a non-zero number, the only time you'll get 0 is when every bit is turned on. (This non-equivalence principle holds true for bitwise AND too, unless you know that you are using strictly the numbers 1 and 0. For bitwise OR, to be certain that it would be equivalent, you'd need to make sure that the underlying representation of 0 is all zeros to use it interchangeably. But don't do that! It'll make your code harder to understand.) 

Now that we have a way of flipping bits, we can start thinking about how to turn off a single bit. We know that we want to leave other bits unaffected, but that if we have a 1 in the given position, we want it to be a 0. Take some time to think about how to do this before reading further. 

We need to come up with a sequence of operations that leaves 1s and 0s in the non-target position unaffected; before, we used a bitwise OR, but we can also use a bitwise AND. 1 AND 1 is 1, and 0 AND 1 is 0. Now, to turn off a bit, we just need to AND it with 0: 1 AND 0 is 0. So if we want to indicate that car 2 is no longer in use, we want to take the bitwise AND of XXXXX1XX with 11111011. 

How can we get that number? This is where the ability to take the complement of a number comes in handy: we already know how to turn a single bit on. If we turn one bit on and take the complement of the number, we get every bit on except that bit:
~(1<<position)
Now that we have this, we can just take the bitwise AND of this with the current field of cars, and the only bit we'll change is the one of the car_num we're interested in.
int set_unused(int car_num)
{
    in_use = in_use & ~(1<<position);
}
You might be thinking to yourself, but this is kind of clunky. We actually need to know whether a car is in use or not (if the bit is on or off) before we can know which function to call. While this isn't necessarily a bad thing, it means that we do need to know a little bit about what's going on. There is an easier way, but first we need the last bitwise operator: exclusive-or.

Bitwise Exclusive-Or (XOR)

There is no boolean operator counterpart to bitwise exclusive-or, but there is a simple explanation. The exclusive-or operation takes two inputs and returns a 1 if either one or the other of the inputs is a 1, but not if both are. That is, if both inputs are 1 or both inputs are 0, it returns 0. Bitwise exclusive-or, with the operator of a carrot, ^, performs the exclusive-or operation on each pair of bits. Exclusive-or is commonly abbreviated XOR. 

For instance, if you have two numbers represented in binary as 10101010 and 01110010 then taking the bitwise XOR results in 11011000. It's easier to see this if the bits are lined up correctly:
01110010 ^
10101010 
--------
11011000
You can think of XOR in the following way: you have some bit, either 1 or 0, that we'll call A. When you take A XOR 0, then you always get A back: if A is 1, you get 1, and if A is 0, you get 0. On the other hand, when you take A XOR 1, you flip A. If A is 0, you get 1; if A is 1, you get 0. 

So you can think of the XOR operation as a sort of selective twiddle: if you apply XOR to two numbers, one of which is all 1s, you get the equivalent of a twiddle. 

Additionally, if you apply the XOR operation twice -- say you have a bit, A, and another bit B, and you set C equal to A XOR B, and then take C XOR B: you get A XOR B XOR B, which essentially either flips every bit of A twice, or never flips the bit, so you just get back A. (You can also think of B XOR B as cancelling out.) As an exercise, can you think of a way to use this to exchange two integer variables without a temporary variable? (Once you've figured it out, check the solution.) 

How does that help us? Well, remember the first principle: XORing a bit with 0 results in the same bit. So what we'd really like to be able to do is just call one function that flips the bit of the car we're interested in -- it doesn't matter if it's being turned on or turned off -- and leaves the rest of the bits unchanged. 

This sounds an awful lot like the what we've done in the past; in fact, we only need to make one change to our function to turn a bit on. Instead of using a bitwise OR, we use a bitwise XOR. This leaves everything unchanged, but flips the bit instead of always turning it on:
void flip_use_state(int car_num)
{
    in_use = in_use ^ 1<<car_num;
}

When should you use bitwise operators?

Bitwise operators are good for saving space -- but many times, space is hardly an issue. And one problem with working at the level of the individual bits is that if you decide you need more space or want to save some time -- for instance, if we needed to store information about 9 cars instead of 8 -- then you might have to redesign large portions of your program. On the other hand, sometimes you can use bitwise operators to cleverly remove dependencies, such as by using ~0 to find the largest possible integer. And bit shifting to multiply by two is a fairly common operation, so it doesn't affect readability in the way that advanced use of bit manipulation can in some cases (for instance, using XOR to switch the values stored in two variables). 

There are also times when you need to use bitwise operators: if you're working with compression or some forms of encryption, or if you're working on a system that expects bit fields to be used to store boolean attributes.

Summary

You should now be familiar with six bitwise operators: 

Works on bits for left argument, takes an integer as a second argument
bit_arg<<shift_arg
Shifts bits to of bit_arg shift_arg places to the left -- equivalent to multiplication by 2^shift_arg
bit_arg>>shift_arg
Shifts bits to of bit_arg shift_arg places to the right -- equivalent to integer division by 2^shift_arg 

Works on the bits of both arguments
left_arg & right_arg
Takes the bitwise AND of left_arg and right_arg
left_arg ^ right_arg
Takes the bitwise XOR of left_arg and right_arg
left_arg | right_arg
Works on the bits of only argument
~arg
Reverses the bits of arg 

Skills and knowledge You also know a couple of neat tricks that you can use when performance is critical, or space is slow, or you just need to isolate and manipulate individual bits of a number.

And you now should have a better sense of what goes on at the lowest levels of your computer.

A Parting Puzzle

One final neat trick of bitwise operators is that you can use them, in conjunction with a bit of math, to find out whether an integer is a power of two. Take some time to think about it, then check out the solution.

Monday, March 18, 2013

【贪心法求解最小生成树之Kruskal算法详细分析】---Greedy Algorithm for MST


【贪心法求解最小生成树之Kruskal算法详细分析】---Greedy Algorithm for MST


http://www.cnblogs.com/yanlingyin/archive/2011/11/16/greedy.html

Greedy Algorithm


贪心算法

一、基本概念:
 
     所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解
     贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
    所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

二、贪心算法的基本思路:
    1.建立数学模型来描述问题。
    2.把求解的问题分成若干个子问题。
    3.对每一子问题求解,得到子问题的局部最优解。
    4.把子问题的解局部最优解合成原来解问题的一个解。

三、贪心算法适用的问题
      贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
    实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。
 
四、贪心算法的实现框架
    从问题的某一初始解出发;
    while (能朝给定总目标前进一步)
    { 
          利用可行的决策,求出可行解的一个解元素;
    }
    由所有解元素组合成问题的一个可行解;
  
五、贪心策略的选择
     因为用贪心算法只能通过解局部最优解的策略来达到全局最优解,因此,一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。
 
六、例题分析
    下面是一个可以试用贪心算法解的题目,贪心解的确不错,可惜不是最优解。
    [背包问题]有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。
    要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
    物品 A B C D E F G
    重量 35 30 60 50 40 10 25
    价值 10 40 30 50 35 40 30
    分析:
    目标函数: ∑pi最大
    约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)
    (1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
    (2)每次挑选所占重量最小的物品装入是否能得到最优解?
    (3)每次选取单位重量价值最大的物品,成为解本题的策略。
    值得注意的是,贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。
    贪心算法还是很常见的算法之一,这是由于它简单易行,构造贪心策略不是很困难。
    可惜的是,它需要证明后才能真正运用到题目的算法中。
    一般来说,贪心算法的证明围绕着:整个问题的最优解一定由在贪心策略中存在的子问题的最优解得来的。
    对于例题中的3种贪心策略,都是无法成立(无法被证明)的,解释如下:
    (1)贪心策略:选取价值最大者。反例:
    W=30
    物品:A B C
    重量:28 12 12
    价值:30 20 20
    根据策略,首先选取物品A,接下来就无法再选取了,可是,选取B、C则更好。
    (2)贪心策略:选取重量最小。它的反例与第一种策略的反例差不多。
    (3)贪心策略:选取单位重量价值最大的物品。反例:
    W=30
    物品:A B C
    重量:28 20 10
    价值:28 20 10
    根据策略,三种物品单位重量价值一样,程序无法依据现有策略作出判断,如果选择A,则答案错误。




动态规划和贪心算法的区别
动态规划和贪心算法都是一种递推算法 
均有局部最优解来推导全局最优解 

不同点: 
贪心算法: 
1.贪心算法中,作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留。 
2.由(1)中的介绍,可以知道贪心法正确的条件是:每一步的最优解一定包含上一步的最优解。 

动态规划算法: 
1.全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解 
2.动态规划的关键是状态转移方程,即如何由以求出的局部最优解来推导全局最优解 
3.边界条件:即最简单的,可以直接得出的局部最优解
==============================================================================
贪心算法与动态规划 
贪心法的基本思路:   
    
从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到某算法中的某一步不能再继续前进时,算法停止。   
该算法存在问题:   
1.   不能保证求得的最后解是最佳的;   
2.   不能用来求最大或最小解问题;   
3.   只能求满足某些约束条件的可行解的范围。实现该算法的过程:   
从问题的某一初始解出发;
   
while   能朝给定总目标前进一步   do   
求出可行解的一个解元素;   
由所有解元素组合成问题的一个可行解 

贪心算法最经典的例子,给钱问题。   
比如中国的货币,只看元,有1元2元5元10元20、50、100   
    
如果我要16元,可以拿16个1元,8个2元,但是怎么最少呢?   
如果用贪心算,就是我每一次拿那张可能拿的最大的。   
比如16,我第一次拿20拿不起,拿10元,OK,剩下6元,再拿个5元,剩下1元   
也就是3张   10、5、1。   
    
每次拿能拿的最大的,就是贪心。   
    
但是一定注意,贪心得到的并不是最优解,也就是说用贪心不一定是拿的最少的张数   
贪心只能得到一个比较好的解,而且贪心算法很好想得到。   
再注意,为什么我们的钱可以用贪心呢?因为我们国家的钱的大小设计,正好可以使得贪心算法算出来的是最优解(一般是个国家的钱币都应该这么设计)。如果设计成别的样子情况就不同了   
比如某国的钱币分为   1元3元4元   
如果要拿6元钱   怎么拿?贪心的话   先拿4   再拿两个1     一共3张钱   
实际最优呢?   两张3元就够了   



求最优解的问题,从根本上说是一种对解空间的遍历。最直接的暴力分析容易得到,最优解的解空间通常都是以指数阶增长,因此暴力穷举都是不可行的。
最优解问题大部分都可以拆分成一个个的子问题,把解空间的遍历视作对子问题树的遍历,则以某种形式对树整个的遍历一遍就可以求出最优解,如上面的分析,这是不可行的。
贪心和动态规划本质上是对子问题树的一种修剪。两种算法要求问题都具有的一个性质就是“子问题最优性”。即,组成最优解的每一个子问题的解,对于这个子问题本身肯定也是最优的。如果以自顶向下的方向看问题树(原问题作根),则,我们每次只需要向下遍历代表最优解的子树就可以保证会得到整体的最优解。形象一点说,可以简单的用一个值(最优值)代表整个子树,而不用去求出这个子树所可能代表的所有值。
动态规划方法代表了这一类问题的一般解法。我们自底向上(从叶子向根)构造子问题的解,对每一个子树的根,求出下面每一个叶子的值,并且以其中的最优值作为自身的值,其它的值舍弃。动态规划的代价就取决于可选择的数目(树的叉数)和子问题的的数目(树的节点数,或者是树的高度?)。
贪心算法是动态规划方法的一个特例。贪心特在,可以证明,每一个子树的根的值不取决于下面叶子的值,而只取决于当前问题的状况。换句话说,不需要知道一个节点所有子树的情况,就可以求出这个节点的值。通常这个值都是对于当前的问题情况下,显而易见的“最优”情况。因此用“贪心”来描述这个算法的本质。由于贪心算法的这个特性,它对解空间树的遍历不需要自底向上,而只需要自根开始,选择最优的路,一直走到底就可以了。这样,与动态规划相比,它的代价只取决于子问题的数目,而选择数目总为1。

Dynamic Programming

Dynamic Programming

http://www.wutianqi.com/?p=539

上次研究一个贪心法问题里的最小生成树模型, 现在再来研究一下动态规划, 因为其实二者在我看来是一会儿事儿. 只不过后者递回的概念更为明显吧.

先简单地陈述一下背包问题:
话说有一个贼, 他背着一个包去偷东西. 这个包的容量是W. 他有种东西可以偷(每种东西有无数个), 它们的重量分别是
w1,..,wn
v1,...vn.
而他最多可以拿k个东西.
我们的任务就是帮他最大化他可以偷的东西的总价值.
一.
把这一切变成符号:
已知有n种东西, 分别有重量 w1,..,wn, 价值 v1,...,vn. 又已知最大重量为W, 要如何取得最大价值呢?

二.
所谓的动态规划, 其实就是要把眼前的问题延迟, 小化. 直到小到你一眼就知道答案为止.
这是一个在计算机科学里非常核心的概念. 归纳法和递进都是如此. 举一个简单的例子, 好比说
你要上楼梯, 但是你不确定自己能不能爬到100层. 但是如果我现在给你两个条件.
1) 你可以从第k层上到第k+1层.
2) 你可以从第0层上到第1层.
那么在完美的数学世界里, 你就可以确定你是可以爬到第100层的. (当然在不完美的现实世界里你可能在第99层就选择跳楼自杀)

那么回到背包问题, 我们就要用到这个概念了.
现在来重温一下我们眼前的问题:
"已知有n种东西, 分别有重量 w1,..,wn, 价值 v1,...,vn. 又已知最大重量为W, 要如何取得最大价值呢?"

注意:
 最大重量为W.


我们要做的就是要让W变小. 我们把眼前的问题拖延一下, 去看看比她们更小的问题.
所以就有了解决动态规划的第一个步骤:

步骤一: 找出子问题.
在这里, 我们的子问题是:
"已知最大重量为w, 要如何取得最大价值呢?"
好了, 现在我们把解决W的问题成功的延后了, 但是要记得该来的总是会来.
我们定义一个函数, 让它为F(w). 但是我们最终是要返回F(W)的.

步骤二:定义递进关系
在爬楼梯的例子里, 从k层总是能上到k+1层就是一个递进关系, 那么这个问题的递进关系又在哪里呢?
在动态规划里, 递进关系通常都是以选择的形态出现. 换言之, 在第i个递进中, 我们总是假设前i-1的解都是正确的, 我们要在这个假设的基础上决定在第i个递进中做出选择.
于是我们这样定义函数:
F(w)
是在最大重量是w, 贼可以获得的最大价值.

回到背包问题, 就是这样的:
那么现在的总重量就是w,而其中包括了东西i, 那么前一次的总重量就是w-wi, 而加上东西i的价值,我们就有,
F(w)=F(w-wi)+vi

但是这个i是哪个东西呢? 我们不知道. 而我们知道i只需要满足一个条件, 那就是wi<w.
所以我们要把符合条件的都检验一遍, 并且选择最大的
F(w)=max { F(w-wi)+vi: wi<w)}

步骤三: 选择基本状态
也就是最初上楼梯的那一步.
背包问题的基本状态很简单, 那就是当最大重量是0的时候, 你什么也拿不走,
也就是说:
F(0)=0

步骤四: 伪代码
这样一来我们就有了我们需要的所有步骤, 就差最后这么一抽了.
伪代码如下:

F(0)=0

for w=1 to W

  F(w)=max { F(w-wi)+vi: wi<=w, if all wi>w return 0}
return F(W)

为了更好地看清动态规划是如何运作的, 我们可以追一个例子来看看.

假如是这样的:
W=10
东西 重量 价值
1 6 30
2 3 14
3 4 16

由于此问题的简单性, 人脑就显示出它的优越性, 我们可以很轻易地看出, 这时候最好的选择是:
1 +3 : 30+16=46
 F(w)=max { F(w-wi)+vi: wi<=w, if all wi>w return 0}
而电脑呢, 它依旧是那么傻,
F(0)=0
w=1
F(1)=0,
F(2)=0,
F(3)=F(0)+14=14,
F(4)=max{F(1)+14, F(0)+16}=max{14,16}=16
F(5)=max{F(2)+14,F(1)+16}=max{14,16)=16
F(6)= max{F(0)+30, F(3)+14,F(2)+16}=max {30,28,16}=30
F(7)=max {F(1)+30,F(4)+14,F(3)+16}=max{30,30,30}=30
F(8)=max{F(2)+30,F(5)+14,F(4)+16}=max{30, 30, 32}=32
F(9)=max{F(3)+30, F(6)+14, F(5)+16)=max{44,44,32}=44
F(10)=max{F(4)+30,F(7)+14,F(6)+16=max{44, 44, 46}=46
------------------------------------------------------------------------------------------------------------------------------
下面我们可以加上一个附加条件, 即是每个东西我们只可以用一次. 这样一来, 我们的函数就多了一个变量, j.
以前我们只需要把W变为w就可以了, 现在我们还需要把n变成j.
这回我们的选择题变为:
目前我们有总重量w-wj的和j-1个东西的情况下, 我们要不要加入商品j呢? 最后返回F(W,n)即可.
如果选择j,那么我们有,
F(w,j)=F(w-wj,j-1)+vj
如果不选择, 那么我们有,
F(w,j)=F(w,j-1)
所以,
F(w,j)=max{F(w-wj,j-1)+vj,F(w,j-1)}
那么基本状态又是什么呢?
很简单:如果没有东西, 是0. 如果没有袋子,也是0. 符号表示就是:
F(0,j)=0 and F(w,0)=0
这样一来我们就可以写伪代码了.
F(0,j)=0 and F(w,0)=0
 for j=1 to n
   for w = 1 to W
    if wj>w F(w,j)=F(w,j-1)//如果东西 j 的重量大于这时的总重量, 肯定不要它
     F(w,j)=max{F(w-wj,j-1)+vj,F(w,j-1)}
return F(W,n)
=========================================================================
那么如果我们再加一个条件, 也就是我们规定只能偷k个东西呢?
很简单, 这时我们不返回F(W,n),而是返回F(W,k) 就好了.
=========================================================================
那么如果我们换一个条件, 也就是说这时候我们每个东西都只可以偷m个,怎么办呢?
我们只要把从1到j变成从1到mj就可以了!最后我们返回 F(W,nm).






背包之01背包、完全背包、多重背包详解 
                                                                                               — Tanky Woo(2010.07.31)
首先说下动态规划,动态规划这东西就和递归一样,只能找局部关系,若想全部列出来,是很难的,比如汉诺塔。你可以说先把除最后一层的其他所有层都移动到2,再把最后一层移动到3,最后再把其余的从2移动到3,这是一个直观的关系,但是想列举出来是很难的,也许当层数n=3时还可以模拟下,再大一些就不可能了,所以,诸如递归,动态规划之类的,不能细想,只能找局部关系。 
hanoi
图1.汉诺塔图片 
(引至杭电课件:DP最关键的就是状态,在DP时用到的数组时,也就是存储的每个状态的最优值,也就是记忆化搜索) 
要了解背包,首先得清楚动态规划: 
动态规划算法可分解成从先到后的4个步骤:
1. 描述一个最优解的结构; 
2. 递归地定义最优解的值; 
3. 以“自底向上”的方式计算最优解的值;
4. 从已计算的信息中构建出最优解的路径。 
其中步骤1~3是动态规划求解问题的基础。如果题目只要求最优解的值,则步骤4可以省略。

背包的基本模型就是给你一个容量为V的背包  在一定的限制条件下放进最多(最少?)价值的东西 
当前状态→ 以前状态 

看了dd大牛的《背包九讲》,迷糊中带着一丝清醒,这里我也总结下01背包,完全背包,多重背包这三者的使用和区别,部分会引用dd大牛的《背包九讲》,如果有错,欢迎指出。  (www.wutianqi.com留言即可) 

首先我们把三种情况放在一起来看: 
01背包(ZeroOnePack): 有N件物品和一个容量为V的背包, 每种物品均只有一件第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 
完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 
多重背包(MultiplePack): 有N种物品和一个容量为V的背包,第i种物品最多有n[i]件可用。每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 
比较三个题目,会发现不同点在于每种背包的数量,01背包是每种只有一件,完全背包是每种无限件,而多重背包是每种有限件。 


先来分析01背包: 
01背包(ZeroOnePack): 有N件物品和一个容量为V的背包,每种物品均只有一件。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。 
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 
用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是: 

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

把这个过程理解下
在前i件物品放进容量v的背包时,它有两种情
情况一: 第i件不放进去,这时所得价值为:f[i-1][v]
情况二: 第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i] 
(第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品) 
最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。  (这里是重点,理解!) 

这里是用二维数组存储的,可以把空间优化,用一维数组存储。 
用f[0..v]表示,f[v]表示把前i件物品放入容量为v的背包里得到的价值。把i从1~n(n件)循环后,最后f[v]表示所求最大值。

这里f[v]就相当于二维数组的f[i][v]。那么,如何得到f[i-1][v]和f[i-1][v-c[i]]+w[i]?(重点!思考)
首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。
即:for i=1..N
现在思考如何能在是f[v]表示当前状态是容量为v的背包所得价值,而又使f[v]和f[v-c[i]]+w[i]标签前一状态的价值? 

逆序

这就是关键! 
1
2
3
for i=1..N
   for v=V..0
        f[v]=max{f[v],f[v-c[i]]+w[i]};
分析上面的代码:当内循环是逆序时,就可以保证后一个f[v]和f[v-c[i]]+w[i]是前一状态的!这里给大家一组测试数据:  测试数据: 10,3 3,4 4,5 5,6 
01pack-1
图2: 01背包图(1)

这个图表画得很好,借此来分析: 
C[v]从物品i=1开始,循环到物品3,期间,每次逆序得到容量v在前i件物品时可以得到的最大值。
请在草稿纸上自己画一画

这里以一道题目来具体看看: 
分析:
01pack-2
图2: 01背包图(2)

具体根据上面的解释以及我给出的代码分析。这题很基础,看懂上面的知识应该就会做了。 


完全背包 
完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。  完全背包按其思路仍然可以用一个二维数组来写出:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

 

同样可以转换成一维数组来表示:
伪代码如下:
1
2
3
for i=1..N
    for v=0..V
        f[v]=max{f[v],f[v-c[i]]+w[i]}

顺序


想必大家看出了和01背包的区别,这里的内循环是顺序的,而01背包是逆序的。
现在关键的是考虑:为何完全背包可以这么写?
在次我们先来回忆下,01背包逆序的原因?是为了是max中的两项是前一状态值,这就对了。 那么这里,我们顺序写,这里的max中的两项当然就是当前状态的值了,为何? 因为每种背包都是无限的。当我们把i从1到N循环时,f[v]表示容量为v在前i种背包时所得的价值,这里我们要添加的不是前一个背包,而是当前背包。所以我们要考虑的当然是当前状态。

这里同样给大家一道题目: 


多重背包 
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程: 

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}

这里同样转换为01背包: 
普通的转换对于数量较多时,则可能会超时,可以转换成二进制(暂时不了解,所以先不讲)  对于普通的。就是多了一个中间的循环,把j=0~bag[i],表示把第i中背包从取0件枚举到取bag[i]件。 




http://www.360doc.com/content/11/1218/16/3725126_173166153.shtml

http://steven-wang.appspot.com/dpa-knapsack-problem-31001.html


http://blog.csdn.net/hhygcy/article/details/3955683