题意:有一些男生女生,男生女生数量差不超过100 ,男生女生两两配对。要求求出一种配对方法,使每一对的高度差的和最小。
思路:(我是真的笨笨笨!!磨磨唧唧写一堆是因为我笨!我看了别人的博客,思路全是学别人的,轻喷!)设人少的一组人数为n,b[],人多的一组人数为m,g[](b[],g[]先排好序),用dp[i][j]表示n中的前i个人与m中的前j个人配对所得到的最小值。
那么dp[i][j]就是min(dp[i-1][k]+|b[i]-g[k]|),就是n中前i-1个人和m中前1~k个人配对的最小值加上b[i]和g[k]的差。
对于每个i,j的范围是(i~i+m-n)
这样j的范围是i~m-n+i,对于dp数组而言,第二维数组要开到m,由于数组过大,而我们又知道,m-n<=100,所以用j表示j-i,j的范围就变成了(0~m-n),也就是(0~100),这种情况下dp[i][j]表示b中前i个人和g中前i+j个人的最小值。
好了,这样就好写了。
代码是
double getdp(int m, int n, double *g, double *b) //m,g为人数多的一组,n,b为人少的一组 { for (int i = 1; i <= n; ++i) { dp[i][0] = dp[i - 1][0] + fabs(b[i] - g[i]); for (int j = 1; j <= m - n; ++j) { dp[i][j] = dp[i - 1][0] + fabs(b[i] - g[i]); for (int k = 1; k <= j; k++) { dp[i][j] = min(dp[i][j], dp[i - 1][k] + fabs(b[i] - g[i + k])); } } } return dp[n][m - n]; }
好了,提交一下TLE……拜托,这也能叫dp?我已经学蒙了orz
看一下时间复杂度是n*(m-n)^2,超时妥妥滴……
这时换一个思路,就是对于每一个dp[i][j]它的可能就是选第i+j个,不选第i+j个数。
如果不选的话,那么dp[i][j]就等于dp[i][j-1]是不(既然不选第i+j个数,相当于不存在呗)~~如果选了呢,就是dp[i-1][j]+|b[i]-g[i+j]|
状态方程:dp[i][j]=min(dp[i][j-1], dp[i-1][j] + |b[i]-g[i+j]|)
这样我们终于可以写出代码了T^T
/** hdu 3392 */ #include <iostream> #include <algorithm> #include <cstring> #include <cmath> using namespace std; const int N = 10005; double a[N], b[N]; double dp[N][110]; double getdp(int m, int n, double *g, double *b) //m,g为人数多的一组,n,b为人少的一组 { for (int i = 1; i <= n; ++i) { dp[i][0] = dp[i - 1][0] + fabs(b[i] - g[i]); for (int j = 1; j <= m - n; ++j) { dp[i][j] = min(dp[i - 1][j] + fabs(b[i] - g[i + j]), dp[i][j - 1]); } } return dp[n][m - n]; } int main() { //freopen("in.txt", "r", stdin); int boys, girls; while (scanf("%d%d", &boys, &girls) != EOF && (boys || girls)) { for (int i = 1; i <= boys; i++) { scanf("%lf", &a[i]); } for (int i = 1; i <= girls; i++) { scanf("%lf", &b[i]); } sort(a + 1, a + 1 + boys); sort(b + 1, b + 1 + girls); double ans; if (boys < girls) ans = getdp(girls, boys, b, a); else ans = getdp(boys, girls, a, b); printf("%f\n", ans); } return 0; }
好了,这回AC了。
但是看了别人的博客,可以用到滚动数组【啊喂!!我就是为了学一下滚动数组才搜到这道题的,结果根本不用啊!】。
滚动数组很神奇啊,因为对于每一个dp[i],求它的过程只与dp[i-1]有关,所以开成2个就够了。既dp[2][...]
代码
/** hdu 3392 */ const int N = 10005; double a[N], b[N]; double dp[2][110]; double getdp(int m, int n, double *g, double *b) //m,g为人数多的一组,n,b为人少的一组 { memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; ++i) { dp[i % 2][0] = dp[(i - 1) % 2][0] + fabs(b[i] - g[i]); for (int j = 1; j <= m - n; ++j) { dp[i % 2][j] = min(dp[(i - 1) % 2][j] + fabs(b[i] - g[i + j]), dp[i % 2][j - 1]); } } return dp[n % 2][m - n]; }
改了之后注意加一句memset(dp, 0, sizeof(dp));
因为窝之前的dp[0][..]是没有用到了,一直是0……所以不用
对于这种通过%N...来节省数组空间的方法窝觉得真是太神奇了orz……
继续努力~