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 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

题意:就是每行选一个,上下两行需满足|j-k|≤f(i,j)+f(i+1,k).,问最小的cell和值。

分析:明显的dp,dp[i][j]表示到第i行选第j个的值,可是这样转移复杂度须要n*m*m,肯定会超时。

我们注意到|j-k|<=f(i,j)+f(i-1,k),那么对于i-1行的第k个我们更新[k-f(i-1,k),k+f(i-1,k)],对于第i行查询[j-f(i,j),j+f(i,j)],这样刚好满足的是要求的条件。

所以就用线段树维护一下查询区间最小值更新区间值就好。这样复杂度就是n*m*log(m)。。

/**
 * @author neko01
 */
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair<int,int> pp;
const double eps=1e-8;
const double pi=acos(-1.0);
const int INF=0x7fffffff;
const LL inf=(((LL)1)<<61)+5;
const int N=105;
const int M=5005;
int a[N][M];
int f[N][M];
int dp[N][M];
struct node{
    int l,r;
    int Min;
    int col;
}tree[M*4];
void build(int x,int l,int r)
{
    tree[x].l=l,tree[x].r=r;
    tree[x].Min=INF;
    tree[x].col=INF;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
inline void push_down(int x)
{
    if(tree[x].col!=INF)
    {
        tree[x<<1].col=min(tree[x].col,tree[x<<1].col);
        tree[x<<1|1].col=min(tree[x<<1|1].col,tree[x].col);
        tree[x<<1].Min=min(tree[x].col,tree[x<<1].Min);
        tree[x<<1|1].Min=min(tree[x].col,tree[x<<1|1].Min);
        tree[x].col=INF;
    }
}
void update(int x,int l,int r,int val)
{
    if(tree[x].l==l&&tree[x].r==r)
    {
        tree[x].Min=min(tree[x].Min,val);
        tree[x].col=min(tree[x].col,val);
        return;
    }
    push_down(x);
    int mid=(tree[x].l+tree[x].r)>>1;
    if(r<=mid) update(x<<1,l,r,val);
    else if(l>mid) update(x<<1|1,l,r,val);
    else
    {
        update(x<<1,l,mid,val);
        update(x<<1|1,mid+1,r,val);
    }
    tree[x].Min=min(tree[x<<1].Min,tree[x<<1|1].Min);
}
int query(int x,int l,int r)
{
    if(tree[x].l==l&&tree[x].r==r)
        return tree[x].Min;
    push_down(x);
    int mid=(tree[x].l+tree[x].r)>>1;
    if(r<=mid) return query(x<<1,l,r);
    else if(l>mid) return query(x<<1|1,l,r);
    else return min(query(x<<1,l,mid),query(x<<1|1,mid+1,r));
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
                if(i==1) dp[1][j]=a[i][j];
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&f[i][j]);
        for(int i=2;i<=n;i++)
        {
            build(1,1,m);
            for(int j=1;j<=m;j++)
            {
                int l=max(1,j-f[i-1][j]);
                int r=min(m,j+f[i-1][j]);
                update(1,l,r,dp[i-1][j]);
            }
            for(int j=1;j<=m;j++)
            {
                int l=max(1,j-f[i][j]);
                int r=min(m,j+f[i][j]);
                dp[i][j]=query(1,l,r)+a[i][j];
            }
        }
        int ans=INF;
        for(int i=1;i<=m;i++)
            ans=min(ans,dp[n][i]);
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-08-25 05:12:34

hdu3698 Let the light guide us dp+线段树优化的相关文章

题解 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

HDU4719-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): 606    Accepted Submission(s): 141 Problem Description N soldiers from the famous "*FFF* army" is standing in a line, from le

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

【uva1502/hdu4117-GRE Words】DP+线段树优化+AC自动机

这题我的代码在hdu上AC,在uva上WA. 题意:按顺序输入n个串以及它的权值di,要求在其中选取一些串,前一个必须是后一个的子串.问d值的和最大是多少. (1≤n≤2×10^4 ,串的总长度<=3*10^5) 题解: 这题一开始我的方向就错了,想了很久d[x][y]表示在AC自动机上的节点x.下一个串要大于y的dp.然而这样做数组要10^4*10^5=10^9级别,开都开不了,妥妥超时. 后来看了一眼题解...觉得自己智商真是感人... 用f[i]表示以第i个串为结尾的时候最大的d值,这样做

ZOJ 3650(多米诺骨牌 dp + 线段树优化)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3650 题意: 给你n个骨牌,每个骨牌有一个在x轴上的位置和高度,每个骨牌可以想左推也可以向右推,问最少多少步可以把骨牌全部推倒. 思路: 之前Goodbye 2014 上的E题就是一道多米诺骨牌的题目,虽然跟这道题目不太一样但是还是提供了一下思路, 附题解的链接: http://blog.csdn.net/u013649253/article/details/4

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),这个显然是过不了的. 转移的复杂度是

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

[Poi2010]Monotonicity 2 (线段树优化DP)

题目描述 给出N个正整数a[1..N],再给出K个关系符号(>.<或=)s[1..k].选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1].求出L的最大值. 输入 第一行两个正整数,分别表示N和K (N, K <= 500,000).第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6).第三行给出K个空格隔开关系符号(>.<或=),第i个表示s[i]. 输出 一个正整数,表示L的

poj 1769 Minimizing maximizer(dp+线段树)

题意:数列长度为n,m次操作(n<=50000,m<=500000),每次操作将区间[si,ti]从小到大排序,求至少使用几次操作使数列的最后一个数与经过所有操作后相等: 思路:选取最少的操作得到最优解,一般采用dp; 假设原数列的第1个数为最大值,dp[j]表示最大值移动到第j个位置需要至少的操作数: 初始令dp[1]=0,dp[j]=inf(j>1); 对于每个i,有dp[ti]=min(dp[ti],min(dp[j](si<=j<=ti))+1); 若复杂度为O(nm