zoukankan      html  css  js  c++  java
  • hdu 3698 UVA1490 Let the light guide us 线段树优化DP

    题目链接 and 题目大意

    hdu3698
    但是 hdu的数据比较弱,所以在这luogu提交吧UVA1490 Let the light guide us
    有一个(n*m)的平原,要求每行选一个点,选(n)个点建造塔楼。
    平原上每个点都有他自己的花费时间和魔法值。

    为了正确控制塔楼,我们必须保证连续两排的每两座塔共用一个共同的魔法区域。
    也就是要求每两行相邻的点都满足如下关系:
    如果第(i)行选(j),第(i+1)行选(k),则需(|j-k|≤f(i,j)+f(i+1,k))
    问花费的总时间最少为多少?
    输入(n,m)
    再输入两个(n*m)的矩阵。
    第一个矩阵 $T[i][j] $表示的是花费时间,
    第二个矩阵 (f[i][j]) 表示的是魔法值

    思路

    如果(m<=100),那么这题就是个(O(n*m{2}))的沙比提

    for (int k = 2; k <= n; ++k) {
    			for (int i = 1; i <= m; ++i) {
    				for (int j = 1 ; j <= m; ++ j) {
    					if (abs(i - j) <= f[k][i] + f[k - 1][j] ) {
    						dp[k][i] = min(dp[k][i], dp[k - 1][j]);
    					}
    				}
    				dp[k][i] += t[k][i];
    			}
    		}
    


    但她并是不,(m<=1000)
    考虑如何优化一下
    $abs(i - j) <= f[k][i] + f[k - 1][j] ( )abs(i-j)(就是)i(和)j(之间的距离 就是)f[k][i] +f[k-1][j]$ 要大于i和j之间的距离(这里可以当成数轴上面)
    我们先看(k)这一行
    他能给下一行(也就是k+1)提供价值的区间至少为([i-f[k][i],i+f[k][i])),或者更大
    我们再看(k+1)这一行
    他能取到的区间(也就是k)的区间至少为([j-f[k+1][j],j+f[k+1][j])),或者更大
    如果他们的区间有交集,则说明他们可以由(k)(k+1)转移
    这个就可以用一颗区间加数,区间求min的线段树维护一下
    复杂度(O(n*mlogm))
    hdu数据很水,要去luogu测!!!
    当然,dp方程你可以压维,但压不压的没有啥意义反正你都开了两个一样大的数组了,多开一个又杂

    两份代码

    暴力代码(n*m*m)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int maxn = 107;
    const int maxm = 5007;
    const int inf = 0x3f3f3f3f;
    int n, m, t[maxn][maxm], f[maxn][maxm], dp[maxn][maxm];
    
    int read() {
    	int x = 0, f = 1; char s = getchar();
    	for (; s < '0' || s > '9'; s = getchar()) if (s == '-') f = -1;
    	for (; s >= '0' && s <= '9'; s = getchar()) x = x * 10 + s - '0';
    	return x * f;
    }
    int abs(int a) {
    	return a > 0 ? a : -a;
    }
    
    int main() {
    	while (233) {
    		// read
    		n = read(), m = read();
    		if (n == 0 && m == 0) break;
    		for (int i = 1; i <= n; ++i)
    			for (int j = 1; j <= m; ++j)
    				t[i][j] = read();
    		for (int i = 1; i <= n; ++i)
    			for (int j = 1; j <= m; ++j)
    				f[i][j] = read();
    
    		//init
    		memset(dp, inf, sizeof(dp));
    		for (int i = 1; i <= m; ++i)
    			dp[1][i] = t[1][i];
    
    		//dp
    		for (int k = 2; k <= n; ++k) {
    			for (int i = 1; i <= m; ++i) {
    				for (int j = 1 ; j <= m; ++ j) {
    					if (abs(i - j) <= f[k][i] + f[k - 1][j] ) {
    						dp[k][i] = min(dp[k][i], dp[k - 1][j]);
    					}
    				}
    				dp[k][i] += t[k][i];
    			}
    		}
    
    		//printf
    		int ans = inf;
    		for (int i = 1; i <= n; ++i)
    			ans = min(ans, dp[n][i]);
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    

    线段树优化(n*m*logm) \

    #include <bits/stdc++.h>
    #define ls rt<<1
    #define rs rt<<1|1
    using namespace std;
    const int maxn=107;
    const int maxm=5007;
    const int inf=0x7fffffff;
    int n,m,a[maxn][maxm],b[maxn][maxm],f[maxn][maxm];
    struct node {
        int l,r;
        int mi,lazy;
    }e[maxm<<4];
    int read() {
        int x = 0, f = 1; char s = getchar();
        for (; s < '0' || s > '9'; s = getchar()) if (s == '-') f = -1;
        for (; s >= '0' && s <= '9'; s = getchar()) x = x * 10 + s - '0';
        return x * f;
    }
    void build(int l,int r,int rt) {
        e[rt].l=l,e[rt].r=r,e[rt].mi=inf,e[rt].lazy=inf;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,ls);
        build(mid+1,r,rs);
    }
    void pushup(int rt) {
        e[rt].mi=min(e[ls].mi,e[rs].mi);
    }
    void pushdown(int rt) {
        if(e[rt].lazy!=inf) {
            e[ls].lazy=min(e[ls].lazy,e[rt].lazy);
            e[rs].lazy=min(e[rs].lazy,e[rt].lazy);
            e[ls].mi=min(e[ls].mi,e[ls].lazy);
            e[rs].mi=min(e[rs].mi,e[rs].lazy);
            e[rt].lazy=inf;
        }
    }
    void update(int L,int R,int k,int rt) {
        if(L<=e[rt].l&&e[rt].r<=R) {
            e[rt].lazy=min(e[rt].lazy,k);
            e[rt].mi=min(e[rt].mi,e[rt].lazy);
            return;
        }
        pushdown(rt);
        int mid=(e[rt].l+e[rt].r)>>1;
        if(L<=mid) update(L,R,k,ls);
        if(R>mid) update(L,R,k,rs);
        pushup(rt);
    }
    int query(int L,int R,int rt) {
        if(L<=e[rt].l&&e[rt].r<=R) {
            return e[rt].mi;
        }
        pushdown(rt);
        int mid=(e[rt].l+e[rt].r)>>1,ans=inf;
        if(L<=mid) ans=min(ans,query(L,R,ls));
        if(R>mid) ans=min(ans,query(L,R,rs));
        pushup(rt);
        return ans;
    }
    void debug1() {
        printf("debug
    ");
        printf("               %d
    ", e[1].mi);
        printf("       %d               %d
    ", e[2].mi, e[3].mi );
        printf("   %d       %d       %d       %d
    ", e[4].mi, e[5].mi, e[6].mi, e[7].mi );
        printf(" %d   %d   %d   %d   %d   %d   %d   %d
    ", e[8].mi,
         e[9].mi, e[10].mi, e[11].mi, e[12].mi, e[13].mi, e[14].mi, e[15].mi);
    }
    void debug()
    {
        for(int i=1;i<=n;++i,puts(""))
            for(int j=1;j<=m;++j)
                cout<<f[i][j]<<" ";
    }
    int main()
    {
        while(1)
        {
            n=read(),m=read();
            if(n==0 && m==0) return 0;
            for(int i=1;i<=n;++i)
                for(int j=1;j<=m;++j)
                    a[i][j]=read();
            for(int i=1;i<=n;++i)
                for(int j=1;j<=m;++j)
                    b[i][j]=read();
            for(int i=1;i<=m;++i)
                f[1][i]=a[1][i];
            for(int i=2;i<=n;++i) {
                build(1,m,1);
                for(int j=1;j<=m;++j) {
                    int l=max(1,j-b[i-1][j]),r=min(m,j+b[i-1][j]);
                    update(l,r,f[i-1][j],1);
                }
                for(int j=1;j<=m;++j) {
                    int l=max(1,j-b[i][j]),r=min(m,j+b[i][j]);
                    f[i][j]=query(l,r,1)+a[i][j];
                }
            }
            int ans=inf;
            for(int i=1;i<=m;++i)
                ans=min(ans,f[n][i]);
            printf("%d
    ", ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    linux父子进程问题
    Raft协议--中文论文介绍
    adb、pm命令操作apk包
    gradle配置
    命令行 更新Android sdk
    Gradle 脚本剪片---copy
    Java数组,去掉重复值、增加、删除数组元素
    注解Annotation 详解(转)
    MAC自带的SVN进行升级
    Android Studio 简单功能介绍
  • 原文地址:https://www.cnblogs.com/dsrdsr/p/9776239.html
Copyright © 2011-2022 走看看