hdu3851.Beat It!

来源:互联网 发布:奥点云 vs 阿里云 编辑:程序博客网 时间:2024/05/12 04:40

http://acm.hdu.edu.cn/showproblem.php?pid=3851

题意:有一只怪兽,在白天攻击一次能损失它pd点血,晚上攻击则损失它pn点血,每次攻击的间隔为t,每天的白天时间与晚上时间分别为t1i, t2i,问N天内最多攻击它多少血。n <= 1000, t <= 100

思路:dp,先把问题等价为n×2段,每段的价值为相应的pd或者为pn,不妨都记为pi,显然每次记录一个时间段最后t时间的攻击状况即可,f[i][j]表示第i个时间段最后一次攻击在第tt[i]-j的最优值,则转移方程为

f[i][j] = max{ g[p[i][j]] + ((tt[i] - j - 1)/t+1) * pi), h[p[i][j]+1][k] + ((tt[i] - j - 1) / t + 1), g[i - 1] + ((tt[i] - j - 1) / t) };

其中,g[i]为前i段所有情况中最优的结果,p[i][j]=max{p`| tt[p` + 1] + tt[p` + 2] + ... + tt[i - 1] + (tt[i] - j - 1) % t >= t }, k满足k + tt[p + 2] + tt[p + 3] + ... + tt[i - 1] + (tt[i] - j - 1) % t = t,h[p][k] = max{ f[p][k], f[p][k + 1], ... , f[p][t - 1]}

这个方程的意思是找以下情况中最大的

(1) 前p段最大的,然后满足tt[p + 1] + tt[p + 2] + ... + tt[i - 1] + (tt[i] - j - 1) % t  >= t, 所以在第i段从任意点开始攻击,最多攻击((tt[i] - j - 1)/t+1)次;

(2) 在第p+1段,最后, 最后一次攻击在第k次,因为 k满足k + tt[p + 2] + tt[p + 3] + ... + tt[i - 1] + (tt[i] - j - 1) % t = t, 所以同理在第i段从任意点开始攻击,最多攻击((tt[i] - j - 1)/t+1)次;

(3) 在第i段放少一个,那么必然可以使得第一段末尾至少可以空出t-1的时间点出来,也就肯定不会跟第i-1的时间段的攻击产生冲突。


列好方程之后首先预处理出p和k,

因为如果固定最后攻击的时间,固定次数(最大可能情况), 那么肯定尽可能放在后面攻击,所以如果对于这样的一个固定的区间,最迟第一次攻击可以在区间(tt[i] - j - 1) % t 后,而且,显然(tt[i] - j1 - 1) % t > (tt[i] - j2 - 1) % 2, p[i][j1] > p[i][j2], 所以先在线性时间内求出所以t2[r], 则p[i][j] = t2[(tt[i] - j - 1) % t ];

k = t - (tt[p + 2] + tt[p + 3] + ... + tt[i - 1] + (tt[i] - j - 1) % t)。

此时转移时间在预处理之后能在O(1)时间内完成,最后时间复杂度为O(nt).

其实若t = 1, pn = pd = 2 ^ 31 - 1, tt[i] = 2 ^ 31. 那么答案应该会超出long long的范围,不过我尝试不写高精度过了。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define sqr(x) ((x) * (x))#define two(x) (1 << (x))#define Rep(i, s, n) for (int i = s; i < (n); ++i)#define X first#define Y secondtypedef long long LL;const int MAXN = 2012;const double eps = 1e-9;const int INF = 1000000000;int n, t, pn, pd, tt[MAXN];LL f[MAXN][200], g[MAXN], ss[MAXN];int pre[MAXN][200], tmp[200];void init(){    scanf("%d%d%d%d", &n, &t, &pd, &pn);    Rep(i, 0, 2 * n) scanf("%d", &tt[i + 1]);    ss[0] = 0;    Rep(i, 0, 2 * n) ss[i + 1] = ss[i] + tt[i + 1];    Rep(i, 1, 2 * n + 1)    {        tmp[0] = 0;        Rep(j, 0, i) if (ss[i - 1] - ss[i - j - 1] >= t)        {            tmp[0] = i - j - 1;            break;        }        Rep(j, 1, min(t, tt[i]))        {            tmp[j] = tmp[j - 1];            while (ss[i - 1] - ss[tmp[j] + 1] + j >= t) ++tmp[j];        }        int rem = tt[i] % t;        Rep(j, 0, min(t, tt[i]))        {            rem = (rem + t - 1) % t;            pre[i][j] = tmp[rem];        }    }}void work(){    g[0] = 0;    Rep(i, 1, n * 2 + 1)    {        LL pt = ((i & 1)? pd: pn);        Rep(j, 0, min(t, tt[i]))        {            f[i][j] = g[pre[i][j]] + pt * ((tt[i] - j - 1) / t + 1);            int rem = (tt[i] - j - 1) % t;            if (rem + ss[i - 1] >= t)                f[i][j] = max(f[i][j], f[pre[i][j] + 1][t - 1 - rem] + pt * ((tt[i] - j - 1) / t + 1));            if (tt[i] - j - 1 >= t)                f[i][j] = max(f[i][j], g[i - 1] + pt * ((tt[i] - j - 1) / t));        }        Rep(j, 1, min(t, tt[i]))        {            f[i][min(t, tt[i]) - j - 1] = max(f[i][min(t, tt[i]) - j - 1], f[i][min(t, tt[i]) - j]);        }        g[i] = max(g[i - 1], f[i][0]);    }    cout << g[n * 2] << endl;}int main(){    int T, ca = 0;    scanf("%d", &T);    while (T--)    {        printf("Case %d: ", ++ca);        init();        work();    }    return 0;}


然后看着今天上海的board,觉得自己没去好幸福……


原创粉丝点击