hdu1500 (排序+单调队列优化 )
从n根筷子里面, 选择k+8个集合的筷子,每个集合三根筷子, A<=B<=C, 费用是(A-B)^2,
问最小的费用是多少。
将n根筷子排序之后,可以知道A和B的下标一定是连续的。
比如有 A B C D , 那么不可能是A C 一个集合, B D一个集合, 因为这样费用反而更大。
设dp[i][j] 为第i个集合的筷子,以j结尾, 就是说 A和B分别是第j和第j-1个筷子
dp[i][j] = min(dp[i-1][k]) + (a[j]-a[j-1])^2
那么如何处理第三根筷子呢?
只要将筷子从大到小排序。
dp[i][j] 的j是从3*i开始的,
而dp[i-1][k]的k是从(i-1)*3开始的,这样就保证了每个集合在前面的筷子中,都有一根筷子与自己配对。
dp[i][j] = min(dp[i-1][k]) + (a[j]-a[j-1])^2 这个循环可以用单调递增队列来优化,保证队头最小就行了。
#pragma warning(disable:4996) #pragma comment(linker, "/STACK:1024000000,1024000000") #include <stdio.h> #include <string.h> #include <time.h> #include <math.h> #include <map> #include <set> #include <queue> #include <stack> #include <vector> #include <bitset> #include <algorithm> #include <iostream> #include <string> #include <functional> #include <unordered_map> typedef __int64 LL; const int INF = 999999999; /* A B 绝逼是连续的,问题是C怎么怎么描述 dp[i][j] 第i套筷子是由j结束 dp[i][j] = min(dp[i][j],dp[i-1][k]) */ const int N = 5000 + 10; int a[N]; int dp[1111][N]; int q[N], head, tail; int main() { int t, n, k; scanf("%d", &t); while (t--) { scanf("%d%d", &k, &n); k += 8; for (int i = 1;i <= n;++i) scanf("%d", &a[i]); std::sort(a + 1, a + n + 1,std::greater<int>()); for (int i = 1;i <= k;++i) { head = tail = 0; for (int j = (i-1)*3;j <= 3 * i - 2;++j) { while (head < tail && dp[i - 1][q[tail - 1]] >= dp[i - 1][j]) tail--; q[tail++] = j; } for (int j = 3 * i; j <= n;++j) { dp[i][j] = (a[j] - a[j - 1])*(a[j] - a[j - 1]) + dp[i - 1][q[head]]; while (head < tail && dp[i - 1][q[tail - 1]] >= dp[i - 1][j-1]) tail--; q[tail++] = j - 1; } } int ans = INF; for (int j = 3 * k;j <= n;++j) ans = std::min(ans, dp[k][j]); printf("%d\n", ans); } return 0; }