Bomber Man wants to bomb an Array.
Accepts: 56
Submissions: 225
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
给一个长度为 NN 的一维格子和一些炸弹的位置,请你计算 “最大总破坏指数”。
每个炸弹都有向左和向右的破坏力,如果一个炸弹向左和向右的破坏力分别为 LL 和 RR,
那么该炸弹将炸毁 L + R + 1L+R+1 个格子(左边LL个,炸弹所在格子,右边RR个)。
破坏指数的计算方式为:所有炸弹炸毁的格子数的乘积。假设第 ii 个炸弹炸毁了 X_iXi个格子,
那么总破坏指数就是 X_1 * X_2 * .... X_mX1∗X2∗....Xm。
现在告诉你每个炸弹的位置,你需要计算 最大的总破坏指数,注意:每个格子最多只允许被炸一次。
输入描述
多组测试数据,第一行为一个整数 T(T \leq 11)T(T≤11)。
每组测试数据第一行为两个整数 N, M(1 \leq N \leq 2000, 1\leq M \leq N)N,M(1≤N≤2000,1≤M≤N),分别表示格子总数和炸弹总数 。
第二行是 MM 个互不相同的数表示每个炸弹所在的位置。
输出描述
对于每组测试数据,输出 floor(10^6 * log2(最大破坏指数)) (floor表示向下取整)。
输入样例
2
10 2
0 9
10 3
0 4 8
输出样例
4643856
5169925
Hint
Sample 1 :![【HDU5653 BestCoder Round 77 (div1) C】【DP 复杂度计算】Bomber Man wants to bomb an Array n炸弹设置爆炸左右界 最大爆炸力的乘积_第1张图片](http://img.e-com-net.com/image/info5/fc388077edc84232913811a63bb4dc59.jpg)
Sample 2: ![【HDU5653 BestCoder Round 77 (div1) C】【DP 复杂度计算】Bomber Man wants to bomb an Array n炸弹设置爆炸左右界 最大爆炸力的乘积_第2张图片](http://img.e-com-net.com/image/info5/72d65eea38af4286973951ee8c8d980a.jpg)
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 2020, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
int a[N];
double f[N][N]; //f[i][j]表示前i个炸弹爆炸,最右界是j,所对应的最大破坏力
double lg[N];
int main()
{
for (int i = 1; i <= 2000; ++i)lg[i] = log(i) / log(2);
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i)scanf("%d", &a[i]), ++a[i];
sort(a + 1, a + n + 1); a[0] = 0; a[n + 1] = m + 1;
f[0][0] = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = a[i]; j <= a[i + 1] - 1; ++j)
{
f[i][j] = 0;
for (int k = a[i - 1] + 1; k <= a[i]; ++k)
{
int len = j - k + 1;
gmax(f[i][j], f[i - 1][k - 1] + lg[len]);
}
}
}
LL ans = floor(f[n][m] * 1000000);
printf("%lld\n", ans);
}
return 0;
}
/*
【trick&&吐槽】
1,炸弹的爆炸范围不能超过别的炸弹
2,贪心原则是炸弹的爆炸范围尽可能平均
3,DP的话,先枚举后爆炸范围,再枚举前爆炸范围
【题意】
有m个格子排成一排。其中n个不同的位置有炸弹。
每个炸弹可以设置其左右爆炸范围,多个炸弹的炸弹范围不可重叠。
一个炸弹的破坏力=其炸坏的格子数
问你所有炸弹破坏力的乘积的最大值val,并输出floor(log2(val)*1e6)
【类型】
DP 复杂度计算
【分析】
我们枚举炸弹
枚举右爆炸范围
枚举左爆炸范围
就可以更新dp[i][j]=max(dp[i-1][l-1]+log2(r-l+1))
答案就是dp[n][m]
看似枚举了三重n
这个复杂度其实只有n^2
【时间复杂度&&优化】
O(n^2)
*/