题意:给你n(n<=40000)个花环,用线串起来,你需要将它挂在天花板上挂m(m<=10000)个位置(如图所示)
每个花环有一定的重量a[i]。也就是说你要把这n个花环分成m-1段。
要满足三个条件:
1、每一段都必须包含偶数个花环。
2、每半段最多d个花环。
3、使花环重量最重的半段重量最小。
注意:最后一段的最后一个点一定要挂在第m个点上(The garland has to be attached to the ceiling in m spots, where the very beginning of the garland should be attached to spot 1 and its end to spot m.)
若无论如何都不满足前两个条件,则输出BAD。
思路:二分+DP。
首先我们来看BAD的情况:
不满足第一个条件则是 n为奇数,不满足第二个条件即为n>2*(m-1)*d,还需要满足上面注意的情况,即n<2*(m-1)时不成立。
要满足第三个条件显然要二分最重的半段的重量。然后就是dp了。不难想到,设dp[i]为前i个花环能挂的最小段数,则
dp[i]=min(dp[i],dp[j]+1) 其中j+1~i这段区间的花环要满足所给条件。
最终dp[n]是否大于m-1即可判断是否符合条件。。。
然而这样却是样例都过不了。。。问题出在哪里呢?
问题就出在这组数据
6 3 10
1 1 100 100 1 1
我们以上思路成立的条件是 段数增加,最重半段的重量减少
但是以上数据 分成一段答案是102,分成两段答案是200
因此dp方程需要修改。
具体怎么修改我没有想到。查了一下题解才反应过来,只需要分段数的奇偶性讨论就可以了。
新的dp方程:(0表示偶数段,1表示奇数段)
dp[i][0]=min(dp[i][0],dp[j][1]+1);
dp[i][1]=min(dp[i][1],dp[j][0]+1);
其中j+1~i为1段,即包含偶数个花环且两个半段花环数量不能超过d。
初始化dp为inf,dp[0][0]=0;
时间复杂度为O(n^2*logn) 给了20000ms的时限需要优化一下,题解是及时break来提高效率,具体见代码
AC代码:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn=40010; int n,m,k,f,d; int sum[maxn],dp[maxn][2];//前i个最少可以分多少段 int ans,cnt,tmp; bool jud(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 len=1;(len<=d)&&(len<=i/2);len++) { if(sum[i]-sum[i-len]>x) break;//这里我用的continue 19000+ms刚好卡过,题解用的break才7000+ms if(sum[i-len]-sum[i-2*len]>x) continue; dp[i][0]=min(dp[i][0],dp[i-2*len][1]+1); dp[i][1]=min(dp[i][1],dp[i-2*len][0]+1); } } if(dp[n][(m+1)%2]>m-1) return 0; return 1; } int main() { int T,cas=1; while(~scanf("%d",&T)) { while(T--) { scanf("%d%d%d",&n,&m,&d); sum[0]=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); sum[i]=sum[i-1]+x; } if(n&1||n<2*(m-1)||n>2*(m-1)*d) { puts("BAD"); continue; } int l=1,r=sum[n]; while(l<r) { int mid=(l+r)>>1; if(jud(mid)) r=mid; else l=mid+1; } printf("%d\n",l); } } return 0; }