这题看完感觉就是DP,然后我当然就用记忆化搜索打了,打着打着,觉得想法不一定对,但打完交了一遍,MLE,改小数组再交,就过了,但其实还没有完全理解透。算是混过的。
相当于DP的方法,dp[i][j]表示前i根筷子组成j组的最小值。我觉得关键的问题在于要先反着排序,然后如果筷子数刚好是人数的3倍时,最小的两根必定会在同一组中,且是一组中的较小的两根。
/* * hdu1500/win.cpp * Created on: 2012-7-27 * Author : ben */ #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <map> #include <stack> #include <string> #include <vector> #include <deque> #include <list> #include <functional> #include <numeric> #include <cctype> using namespace std; const int MAXN = 5005; const int MAXK = 1002; const int MAX = 0x7fffffff; int chops[MAXN]; int dp[MAXN][MAXK]; int dfs(int i, int j) { if(dp[i][j] >= 0) { return dp[i][j]; } if(i == j * 3) { int ret1 = chops[i - 2] - chops[i - 1]; ret1 *= ret1; ret1 += dfs(i - 2, j - 1); dp[i][j] = ret1; }else if(i > j * 3){ int ret1 = dfs(i - 1, j); int ret2 = chops[i - 1] - chops[i - 2]; ret2 *= ret2; ret2 += dfs(i - 2, j - 1); dp[i][j] = ret1 < ret2 ? ret1 : ret2; }else { dp[i][j] = MAX; } return dp[i][j]; } int main() { #ifndef ONLINE_JUDGE freopen("data.in", "r", stdin); #endif int T, K, N; scanf("%d", &T); while(T--) { scanf("%d%d", &K, &N); for(int i = 0; i < N; i++) { scanf("%d", &chops[i]); } sort(chops, chops + N, greater<int>()); memset(dp, -1, sizeof(dp)); for(int i = 0; i < N; i++) { dp[i][0] = 0; } int ans = dfs(N, K + 8); printf("%d\n", ans); } return 0; }