zoukankan      html  css  js  c++  java
  • POJ 1160 Post Office(四边形不等式优化)

    四边形不等式

    函数w满足

    1: 区间包含的单调性,对于(x1<x2<y1<y2),有(w[x2][y1] < w[x1][y2])
    2: 四边形不等式,对于(x1<x2<y1<y2),有(w[x1][y1]+w[x2][y2] < w[x1][y2]+w[x2][y1])

    则函数m(其最优选择)也满足四边形不等式.

    对于满足四边形不等式的函数,是满足单调性的(x方向单调,y方向也单调),可以通过判断dp是否满足四边形不等式,也可以用来优化转移区间

    对于形如 (dp[i][j] = min(dp[i][k]+dp[k+1][j])(ileq kleq j) + w[i][j]) 的区间dp转移一般复杂度为O(n^3)的,
    但若(dp[i][j])满足四边形不等式,则其(s[i][j])(使dp[i][j]最小的k)也满足四边形不等式,即可有O(n^2)的转移
    (dp[i][j] = min(dp[i][k]+dp[k+1][j])(s[i][j-1]leq kleq s[i+1][j])+w[i][j])

    石子合并

    • 题意: 有一圈石子,每次可以合并相邻的两个,得到一个新石子,其权重为原来两个石子权和,记为该次合并的得分,问将一圈石子合并成一堆的最小与最大得分
    • 思路: 区间dp,最小值满足四边形不等式,最大值不满足,因为是一圈,要断环为链跑1~2n上的合并
    using namespace std;
    const int N = 200+10;
    
    int n;
    int a[N],sum[N];
    int dp[N][N],s[N][N];
    int ans1 = 1<<30,ans2= 0;
    int main(){
    	while(scanf("%d",&n)==1){
    	for(int i=1;i<=n;++i)	scanf("%d",&a[i]),a[i+n]=a[i];
    	for(int i=1;i<=n*2;++i)	sum[i] = sum[i-1]+a[i];
    	for(int i=1;i<=n*2;++i)	dp[i][i]=0,s[i][i] = i;
    	for(int len=2;len<=n;++len){
    		for(int i=1;i+len-1<=n*2;++i){
    			int j = i+len-1;
    			dp[i][j] = 1<<30;
    			for(int k=s[i][j-1];k<=s[i+1][j];++k){
    				int val = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
    				if(val < dp[i][j]){
    					dp[i][j] = val;
    					s[i][j] = k;
    				}
    			}
    		}
    	}
    	ans1 = dp[1][n];
    	for(int i=1;i<=n;++i)	ans1 = min(ans1,dp[i][i+n-1]);
    	for(int i=1;i<=n*2;++i)	dp[i][i]=0;
    	for(int len=2;len<=n;++len){
    		for(int i=1;i+len-1<=n*2;++i){
    			int j = i+len-1;
    			dp[i][j] = 0;
    			for(int k=i;k<j;++k){
    				int val = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
    				if(val > dp[i][j]){
    					dp[i][j] = val;
    				}
    			}
    		}
    	}
    	ans2 = dp[1][n];
    	for(int i=1;i<=n;++i)	ans2 = max(ans2,dp[i][i+n-1]);
    	printf("%d
    %d
    ",ans1,ans2);	
    	}
    	return 0;
    }
    
    

    POJ 1160 Post Office

    • 题意: 一条直线上n个点,要选m个点,求每个点距离选择点总和的最小值
    • 思路: 先算l到r只选一个房子的距离,然后区间dp枚举k,前面选m-1个房子,后面选1个房子,k需要用四边形不等式优化

    当前dis可以通过前一个dis和当前点位置O(1)计算出

    
    const int N = 300+10, M = 32;
    
    int n,m,pos[N],dis[N][N];	// i到j选一个点作为邮局最短距离
    int dp[N][M],s[N][M];		// 1到i选j个点作为邮局的最短距离,和最优转移点
    
    int main(){
    	scanf("%d%d",&n,&m);
    
    	for(int i=1;i<=n;++i)	scanf("%d",&pos[i]);
    	for(int i=1;i<=n;++i){
    		dis[i][i] = 0;
    		for(int j=i+1;j<=n;++j){	// 两点之间肯定要选中位数所在点
    			dis[i][j] = dis[i][j-1] + pos[j] - pos[(i+j)>>1];	// dis只增加了右端点到中间点的距离
    		}
    	}
    	for(int i=1;i<=n;++i)
    		dp[i][1] = dis[1][i],s[i][1] = 0;	// 初始化  1到i选一个点的最短距离即为 dis[1][i], 而只选了一个点没有转移点
    	// 	s[j][i-1] <=  s[j][i] <= s[j+1][i]   j从大到小,i从小到大	
    	for(int i=2;i<=m;++i){ 	// 当前选了几个点
    		s[n+1][i] = n;		// 这个值不会被计算,但要被用到,赋为n
    		for(int j=n;j>i;--j){	// 当前有几个房子
    			dp[j][i] = 1<<30;
    			for(int k=s[j][i-1];k<=s[j+1][i];++k){
    				int val = dp[k][i-1] + dis[k+1][j];
    				if(dp[j][i]> val) dp[j][i] = val, s[j][i] = k;
    			}
    		}
    	}
    	printf("%d
    ",dp[n][m]);
    }
    
    
  • 相关阅读:
    1-素材库同步:将素材组的素材同步到oss
    MongoDB_2:mongodb高级聚合查询
    关于python:如果键存在,则删除字典项
    Kafka学习-分布式日志系统 / 消息队列
    摘要算法—md5简介
    mac使用pyenv安装和管理多个python版本
    如何mac电脑上查看安装了几个python?
    mac os-Homebrew的安装及使用
    第一次博客
    个人介绍
  • 原文地址:https://www.cnblogs.com/xxrlz/p/11449466.html
Copyright © 2011-2022 走看看