题解 HDU 3698 Let the light guide us Dp + 线段树优化

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

Let the light guide us

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others)
Total Submission(s): 759    Accepted Submission(s): 253

Problem Description

Plain of despair was once an ancient battlefield where those brave spirits had rested in peace for thousands of years. Actually no one dare step into this sacred land until the rumor that “there is a huge gold mine underneath the plain” started to spread.
Recently an accident destroyed the eternal tranquility. Some greedy fools tried using powerful bombs to find the hidden treasure. Of course they failed and such behavior enraged those spirits--the consequence is that all the human villages nearby are haunted by ghosts.
In order to stop those ghosts as soon as possible, Panda the Archmage and Facer the great architect figure out a nice plan. Since the plain can be represented as grids of N rows and M columns, the plan is that we choose ONLY ONE cell in EACH ROW to build a magic tower so that each tower can use holy light to protect the entire ROW, and finally the whole plain can be covered and all spirits can rest in peace again. It will cost different time to build up a magic tower in different cells. The target is to minimize the total time of building all N towers, one in each row.
“Ah, we might have some difficulties.” said Panda, “In order to control the towers correctly, we must guarantee that every two towers in two consecutive rows share a common magic area.”
“What?”
“Specifically, if we build a tower in cell (i,j) and another tower in cell (i+1,k), then we shall have |j-k|≤f(i,j)+f(i+1,k). Here, f(i,j) means the scale of magic flow in cell (i,j).”
“How?”
“Ur, I forgot that you cannot sense the magic power. Here is a map which shows the scale of magic flows in each cell. And remember that the constraint holds for every two consecutive rows.”
“Understood.”
“Excellent! Let’s get started!”
Would you mind helping them?

Input

There are multiple test cases.
Each test case starts with a line containing 2 integers N and M (2<=N<=100,1<=M<=5000), representing that the plain consists N rows and M columns.
The following N lines contain M integers each, forming a matrix T of N×M. The j-th element in row i (Tij) represents the time cost of building a magic tower in cell (i, j). (0<=Tij<=100000)
The following N lines contain M integers each, forming a matrix F of N×M. The j-th element in row i (Fij) represents the scale of magic flows in cell (i, j). (0<=Fij<=100000)
For each test case, there is always a solution satisfying the constraints.
The input ends with a test case of N=0 and M=0.

Output

For each test case, output a line with a single integer, which is the minimum time cost to finish all magic towers.

Sample Input

3 5

9 5 3 8 7

8 2 6 8 9

1 9 7 8 6

0 1 0 1 2

1 0 2 1 1

0 2 1 0 2

0 0

Sample Output

10

Source

2010 Asia Fuzhou Regional Contest

分析:

这题显然是Dp .....dp[i][j] = min{dp[i + 1][k] + T[i][j]} |j-k|≤f(i,j)+f(i+1,k)

可是,一看数据规模,time有点捉急额、、、

斜率优化? .....没看出来。

四边形不等式? .....还是算了吧。

看来要优化状态转移方程“有点”困难......

让我们换个思路,能不能快速取到合适的k…

|j-k|≤f(i,j)+f(i+1,k)

Dp[i][j]的最优值,一定在以j为圆心,f[i][j]为半径的区间内

而dp[i-1][j]能更新以j为圆心,f[i-1][j] 为半径的区间。

这样,我们用每个dp[i-1][j] 更新每个[j – f[i - 1][j] , j + f[i - 1][j]]范围内的值。

计算 Dp[i][j]时,只需查询[j – f[i][j] , j + f[i][j]]范围内的最小值,既RMQ

So。。。。看代码吧。。。。

AC Code:    呵呵,在HDU上排名第13.......

#include <cstdio>

using namespace std;

const int maxn = 105;

const int maxm = 5005;

const int inf = 1 << 30;

inline int L(int rt)    {return rt << 1;}

inline int R(int rt)    {return rt << 1 | 1;}

inline int min(int a,int b) {return a < b ? a : b;}

struct Node

{

    int l,r;

    int min;        // 区间最小值

    int val;

} nd[maxm << 2];

int f[maxn][maxm];

int time[maxn][maxm];

int dp[maxn][maxm];

inline void pushDown(int rt)

{

    if(nd[rt].val != inf)                            // val 有修改

    {

        nd[L(rt)].val = min(nd[L(rt)].val,nd[rt].val);

        nd[R(rt)].val = min(nd[R(rt)].val,nd[rt].val);

        nd[L(rt)].min = min(nd[L(rt)].min,nd[L(rt)].val);

        nd[R(rt)].min = min(nd[R(rt)].min,nd[R(rt)].val);

        nd[rt].val = inf;

    }

}

void build(int l,int r,int rt)                            // 建线段树

{

    nd[rt].l = l;   nd[rt].r = r;

    nd[rt].min = nd[rt].val = inf;

    if(l == r)  return;

    int mid = (l + r) >> 1;

    build(l,mid,L(rt));

    build(mid + 1,r,R(rt));

}

void update(int l,int r,int val,int rt)                     // 更新区间[l,r]

{

    if(l <= nd[rt].l && nd[rt].r <= r)

    {

        nd[rt].val = min(val,nd[rt].val);

        nd[rt].min = min(nd[rt].val,nd[rt].min);

        return;

    }

    int mid = (nd[rt].l + nd[rt].r) >> 1;

    pushDown(rt);

    if(l <= mid)    update(l,r,val,L(rt));

    if(r > mid)     update(l,r,val,R(rt));

    nd[rt].min = min(nd[L(rt)].min,nd[R(rt)].min);

}

int query(int l,int r,int rt)                            // RMQ

{

    if(l <= nd[rt].l && nd[rt].r <= r)

        return nd[rt].min;

    int ret = inf;

    int mid = (nd[rt].l + nd[rt].r) >> 1;

    pushDown(rt);

    if(l <= mid)    ret = min(ret,query(l,r,L(rt)));

    if(r > mid)     ret = min(ret,query(l,r,R(rt)));

    return ret;

}

inline void scan(int &n)                     // 读入加速

{

    char c;

    while(c = getchar(),c < ‘0‘ || c > ‘9‘);

    n = c - ‘0‘;

    while(c = getchar(),(c >= ‘0‘ && c <= ‘9‘)) n = n * 10 + c - ‘0‘;

}

int main()

{

    int n,m;

    while(scanf("%d%d",&n,&m) && (n != 0 || m != 0))

    {

        for(int i = 1;i <= n;i ++)

        for(int j = 1;j <= m;j ++)

            scan(time[i][j]);

        for(int i = 1;i <= n;i ++)

        for(int j = 1;j <= m;j ++)

            scan(f[i][j]);

        for(int i = 1;i <= m;i ++)              // 初始化边界

            dp[1][i] = time[1][i];

/////////////////////////////////////////////////////核心/////////////////////////////////////////////////////

        for(int i = 2;i <= n;i ++)

        {

            build(1,m,1);

            for(int j = 1;j <= m;j ++)

                update(j - f[i - 1][j],j + f[i - 1][j],dp[i - 1][j],1);

            for(int j = 1;j <= m;j ++)

                dp[i][j] = query(j - f[i][j],j + f[i][j],1) + time[i][j];

        }

/////////////////////////////////////////////////////END/////////////////////////////////////////////////////

        int ans = inf;

        for(int i = 1;i <= m;i ++)

            ans = min(ans,dp[n][i]);

        printf("%d\n",ans);

    }

    return 0;

}

题解 HDU 3698 Let the light guide us Dp + 线段树优化,布布扣,bubuko.com

时间: 2024-10-10 05:22:21

题解 HDU 3698 Let the light guide us Dp + 线段树优化的相关文章

hdu3698 Let the light guide us dp+线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 821    Accepted Submission(s): 285 Problem Description Plain of despair was

hdu 4719 Oh My Holy FFF(dp线段树优化)

Oh My Holy FFF Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 848    Accepted Submission(s): 219 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

HDU 3698 Let the light guide us

Let the light guide us Time Limit: 2000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID: 369864-bit integer IO format: %I64d      Java class name: Main Plain of despair was once an ancient battlefield where those brave spirits

hdu 3450 离散化+dp+线段树优化

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3450 题意: 给你一串长度为n的序列,给一个d,要求找出有几个子序列能够满足两个相邻的元素之间差值不超过d. 思路: dp.定义dp[i]表示以第i个为结束的满足条件的子序列的个数. 转移方程:dp[i]=(∑i?1j=1dp[j])+1(abs(num[i]?num[j])<=d) 答案就是dp数组的总和最后扣掉n就可以了. 此时会发现更新的时间复杂度是O(n2),这个显然是过不了的. 转移的复杂度是

hdu 4893 (多校1007)Wow! Such Sequence!(线段树&amp;二分&amp;思维)

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 352    Accepted Submission(s): 104 Problem Description Recently, Doge got a funny birthday present from his new friend, Prot

hdu 4970 killing monster 代代相传刷qq 不用线段树啦~

[题意]塔防游戏,一条n长的路上,有m个炮台,可以覆盖[li,ri]范围,威力ci,即每一秒,炮塔可以对范围 内的怪物可以造成ci点伤害.只有有q只怪物,每只怪物有hi点血,出现位置为xi:当怪物血量减少到0或以下时消失,怪物一直朝n位置前进.问有几只怪物可以离开这条路. [题解]用线段树可以做,不过还好我们有代代相传的刷qq 算法 ,让解法变得简单的多~    ^_^ 1 #include <iostream> 2 #include <cstdio> 3 #include <

HDU 5023 A Corrupt Mayor&#39;s Performance Art 线段树区间更新+状态压缩

Link:  http://acm.hdu.edu.cn/showproblem.php?pid=5023 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <string> 7 #include <cmath> 8 using namesp

hdu 5023 A Corrupt Mayor&#39;s Performance Art(线段树区间更新)

#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; int tree[5001000],add[5001000]; int color[50]; int n,m; void pushup(int pos) { tree[pos]=tree[pos<<1]|tree[pos<<1|1]; //更新

hdu 5023 A Corrupt Mayor&#39;s Performance Art (线段树)

把求和操作改为或操作,就可以了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 using namespace std; 9 const int ma