UVa1443
// UVa 1443 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 40000 + 5; const int INF = 1000000003; int n, m, d; int sum[maxn], dp[maxn][2]; bool check(int x) { for (int i = 0; i <= n; ++i) dp[i][0] = dp[i][1] = INF; dp[0][0] = 0; for (int i = 2; i <= n; i+=2) for (int j = 1; (j<=d) && (j<=i/2); j++) { if (sum[i]-sum[i-j] > x) break; if (sum[i-j]-sum[i-2*j] > x) continue; dp[i][0] = min(dp[i][0], dp[i-j*2][1]+1); dp[i][1] = min(dp[i][1], dp[i-j*2][0]+1); } return dp[n][(m+1)%2] <= m-1; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d%d", &n, &m, &d); sum[0] = 0; for (int i = 1; i <= n; ++i) { int w; scanf("%d", &w); sum[i] = sum[i-1] + w; } if (n&1 || n>2*d*(m-1) || n<2*(m-1)) { printf("BAD\n"); continue; } int l = 1, r = sum[n]; while (l < r) { int m = (l+r) / 2; if (check(m)) r = m; else l = m+1; } printf("%d\n", l); } return 0; }
动态规划辅助其他算法
这道题要我们求最小半段重量的最大值,最小求最大、最大求最小等等问题让我们想到了二分答案,通过枚举不同的答案判断是否
满足来找到最优解,所以我们二分最大值是多少,判断是否满足,判断的过程需要动态规划,贪心能不能完成我不太确定,
如果有人知道贪心的解法可以私信或者留言给我,那么看一看动态规划怎么解决吧!
首先我不考虑无解的情况,对于有解时我们才采用动态规划,我们想一想,如果满足半段重量小于或是等于我们枚举的值的话,
那么下面我们关注的就是数量了,尽量满足固定点数量少,如果能满足固定点数量少(<=m),那么一定可以满足这个问题,
所以状态定义就是用d(i)表示串前i个花朵满足每一个半段满足重量小于等于枚举的量并且每一个半段的数量都不超过d朵花时的最少
固定点数。
下面是状态分析:
我们知道,i一定是偶数,如果是奇数的话后面的状态一定不会考虑这个状态(同时也不符合题意),然后再d这个范围内,将j-i区间作为新的
整段,然后看看半段是否满足。
下面是状态转移方程:
d(i) = min(d(j)+1|i-2*d+1<=j<i && i-j+1%2=0 && 每个区间<=枚举量)
上面的转移方程可能还有细节没有显示的地方,然后说一下无解的情况,有两种:
1、n为奇数
2、n > 2*d*(m-1)
下面是代码实现:
细节问题,写程序时,这个样例过不去:
6 3 10 1 1 100 100 1 1
我的输出102 答案200
这说明满足固定点小于等于m并不能够完全说明符合答案,就是说我分成了1 1 100 与 100 1 1两半段,自然小于m
但是根据题意所加几个固定点要不就是不满足偶数,要不就不满足102的个答案,所以需要修改程序。
下面是思路:
我们再不改变原来要求(固定点尽量少)的情况下,需要给状态的定义增加一维的空间,即分成奇数段还是偶数段,
如果m-1(段数)的奇数,我们也从奇数段与m-1比较,如果是偶数,我们用偶数取比较,至于为什么,我还在想,
可能在以后会发证明的想法。(如果你们有想法,欢迎留言)。