Solution to knapsack problem exceeds time and RAM limits
Part of the reason you're running out of memory is that you are allocating all of your items into memory twice. Once in the two lists weights and values, and once again in hash_table.

Looking at your program, I don't see a need for you to keep your weights and values allocated as you do. You iterate through them one at a time in your outer for loop. What I'd do is make use of a generator and wrap the file reading in a function:

def read_file():
    with open('knapsack.txt') as fh:

        # these value names are a bit more descriptive, and
        # you can use a next() call on this generator
        # to extract these values
        WEIGHT, SIZE = map(int, next(fh).strip().split())
        yield WEIGHT, SIZE

        for line in fh:
            yield map(int, line.strip().split())


This way you can do tuple unpacking in a for loop:

iterator = read_file()
# here's that next call I mentioned
WEIGHT, SIZE = next(iterator)

# iterate over the remaining values
for weight, value in iterator:
    # do something



This will keep copies of your values from proliferating throughout the execution of your program when you really don't need them.

I'd also look into enumerate, since you need the index for part of your hash_table keys, but you also need the weight and value as well. This eliminate repeated lookups that slow down your code:

for i, (w, v) in enumerate(read_file(), start=1):
    for x in range(WEIGHT + 1):
        ...


To show the effect:

# repeated index lookup
python -m timeit -s 'x = list(range(1, 10000))' 'for i in range(len(x)): a = x[i] + 2'
500 loops, best of 5: 792 usec per loop

# no repeated index lookup
python -m timeit -s 'x = list(range(1, 10000))' 'for i, j in enumerate(x): a = j + 2'
500 loops, best of 5: 536 usec per loop


It doesn't appear that you really need the leading 0 0 row on weights and columns, either, since you start the index at 1, skipping it. Avoiding the addition of lists here cuts down on overhead, and you can specify enumerate to start at a given value with the start kwarg as I've done above.

The goal here should be to iterate over a collection as little as possible, so a refactored version might look like:

def read_file():
    with open('knapsack.txt') as fh:

        # these value names are a bit more descriptive, and
        # you can use a next() call on this generator
        # to extract these values
        WEIGHT, SIZE = map(int, next(fh).strip().split())
        yield WEIGHT, SIZE

        for line in fh:
            yield map(int, line.strip().split())


iterator = read_file()
WEIGHT, SIZE = next(iterator)
hash_table = {(0, i): 0 for i in range(WEIGHT + 1)}

for i, (w, v) in enumerate(iterator, start=1):
    for j in range(WEIGHT + 1):
        if w &gt; j:
            hash_table[(i, j)] = hash_table[(i - 1, j)]
        else:
            hash_table[(i, j)] = max(
                hash_table[(i - 1, j)],
                hash_table[(i - 1, j - w)] + v
            )


This doesn't avoid all of the memory issues, however. You are dealing with a relatively large file and housing that in a dictionary will lead to heavy memory usage. As noted in the Wikipedia article, the solution you have implemented will have a worst-case space complexity of O(nW), which for this file is approximately O(n * 2000000)