CSU 1248: 非变性聚丙烯酰胺凝胶电泳
动规题,方法很巧。很像背包问题,这道题我们可以想成给了空间M,让你求价值之和与M的差最小的那个和,如果超过了M的某个和与小于M的某个和与M之差相等,取小的那个,而我们的方法是将背包空间扩充两倍,初始化f[0][0] = 1;f[i][0] = 0;我们利用01背包的知识求出2M空间的所有值,然后从f[N][j]中以M为中心向两边找,找到第一个为1的f[N][j](如果两边相等取小的),即为所求。(从整个空间开始不断的减少(增加)空间,看最近的哪一个恰好能够装满)。
代码如下:
#include<stdio.h> #define MAXN 2010 int f[MAXN][MAXN], A[1010]; int M,N; void solve() { f[0][0] = 1; for(int i = 1; i <= 2*M; i ++) { f[0][i] = 0; } for(int i = 1; i <= N;i ++) { for(int j = 0; j <= 2*M; j ++) { f[i][j] = f[i-1][j]; if(j >= A[i]) f[i][j] = f[i][j] || f[i-1][j-A[i]]; } } for(int i = 0;; i ++) if(f[N][M - i] || f[N][M + i]) { if(f[N][M - i]) {printf("%d\n", M - i + 18);} else printf("%d\n",M + i + 18); break; } } void input() { while(scanf("%d%d",&N,&M) == 2) { M -= 18; for(int i = 1; i <= N; i ++) { scanf("%d",&A[i]); A[i] -= 18; } solve(); } } int main() { input(); return 0; }