About Search & DP
sanpin
03/24/08
序
DP与其它算法有关系,当然包括了搜索。
DP与DFS
记忆化递归的实现大家都用过,如果是递归实现的是DFS,就是这里说的了,不再举例。
Dp与BFS
记忆化搜索,怎么不包括BFS呢。这种题大家也做过,只是没意识到。
一个最朴素的BFS效率并不是很高,大家都很自觉地为它加了剪枝之类。有一类重要的剪枝就是记忆,如使用Hash等。经典BFS实现中的“color”,是一体现。再举一例。
例:
Johnny Hates Math(http://acm.pku.edu.cn/JudgeOnline/problem?id=3516)
description
Johnny is on probation! He has failed so many math courses and the Department has forced him to register in a remedial math course. He must pass the course or he’d be expelled from the University. In an attempt to impress his professor, Johnny is typing all his assignments on the computer. The latest assignment is rather simple, Johnny was given a set of problems to solve. Each problem had a list of one or more numbers that Johnny was supposed to add. Johnny has worked all night on the assignment, neatly typing his solution to each problem using a word processor as seen here:
4+12+3=19
As usual, Johnny woke up late, he hardly had the time to print the assignment and rush to class. Only in the classroom did he discover that, due to a printer driver problem, non of the plus signs were printed. The above line was printed as:
4123=19
Write a program to figure out where the pluses are supposed to be. All what Johnny remembers is that all the numbers were positive; None of the numbers, other than possibly the sum, had more than 5 digits; And none of the numbers had a zero as the left-most digit.
Input
Your program will be tested on one or more expressions. Each expression is specified on a single line. No line will be longer than 256 characters. The last line, which is not part of the test cases, will be 0=0.
Output
For each expression in the input, your program must print a line of the form:
k. result
Where k is the expression number (starting at 1), and result is the expression with the necessary plus signs in place. There are no spaces in result. If there are more than one possible solution, print a solution that requires the least number of plus signs. Knowing how bad Johnny is in arithmetic, it is possible that there is no solution, in which case your program should print "IMPOSSIBLE" as the result.
Sample Input
4123=19 15442147612367219875=472 111=8 0=0
Sample Output
1. 4+12+3=19 2. 15+44+21+47+61+23+67+21+98+75=472 3. IMPOSSIBLE 明显可以DP做.A[i]表到第i个可以生成的值的集合,易有转移方程。但问题是这集合如何表示?可以像邻接表一样的形式,应该是可以的。A[i]中会不会有大量重复元素呢?这是可能的,如(111111111……)在后面就有大量重复了,怎么办?去重吧。可以寻找一个Hash函数,开一张大大的表来实现。 上述步骤实际上是实现了一BFS。可以用明显的BFS的形式来实现,更清晰。 这题的无聊之处在数据暴大,私以为没必要这样。
Source Code
Problem: 3516 |
| User: sanpin |
Memory: 151592K |
| Time: 7985MS |
Language: G++ |
| Result: Accepted |
-
#include <set> #include <iostream> #define M 300 #define MAX_LEN 10000000 #define HASH_SIZE 500000 #include <algorithm> using namespace std; int num[M],len; int res; set<pair<int,int> > rec; bool hash[HASH_SIZE][257]; struct Node{ int prev; int currValue; int endPosInNum; }; Node que[MAX_LEN]; int rear,front; // sum[i] is sum till i-1 + int sum[M]; void preproc(){ sum[len] =0 ; for (int i=len-1; i>=0; --i) sum[i] = sum[i+1] + num[i]; } bool expand(int s){ int i; int st=que[s].endPosInNum+1; int en = min(st+4,len-1); if (num[st] ==0) return true; int tmp = 0; int tt; for (i=st; i<=en; ++i){ tmp = (tmp<<3) + (tmp<<1) + num[i]; tt = que[s].currValue + tmp; if ((tt + sum[i+1])>res)break; if (hash[tt%HASH_SIZE][i] == true) continue; hash[tt%HASH_SIZE][i] = true; que[rear].prev = s; que[rear].currValue = tt; que[rear].endPosInNum = i; if (i==len-1 && que[rear].currValue == res)return false; ++rear; if (rear >=MAX_LEN) while(1); } return true; } void print(int s){ int p=que[s].prev; if (p ==0){ for (int i=0; i<=que[s].endPosInNum; ++i) printf("%d",num[i]); return; } print(p); printf("+"); for (int i=que[p].endPosInNum+1; i<=que[s].endPosInNum;++i) printf("%d",num[i]); } void bfs(){ memset(hash,false,sizeof(hash)); front = rear = 0; que[0].prev = -1; que[0].currValue = 0; que[0].endPosInNum = -1; ++rear; while (front !=rear){ if (!expand(front)) break; ++front; } if (front == rear){ printf("IMPOSSIBLE/n"); }else{ print(rear); printf("=%d/n",res); } } int main(){ int i,iCase=0; char in[M]; while (1){ scanf("%s",in); if (in[0]=='0')break; ++iCase; printf("%d. ",iCase); for (i=0; in[i]!='=';++i){ num[i] = in[i] - '0'; } len = i; ++i; res = atoi(in+i); preproc(); bfs(); } }
其它的Search
也有吧,一时没想起对应题目来。