由于,这两题有着似乎一样的解法所以将其放在一起总结比較,以达到更好的区分二者的差别所在。

一、区间DP



uva的Cutting Sticks是一道典型的模板题。

题目描写叙述:

有一根长度为l的木棍,木棍上面有m个分割点,每一次分割都要付出当前木棍长度的代价,问如何分割有最小代价。

区间DP的定义:

区间动态规划问题一般都是考虑。对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用。将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合,求合并后的最优值。

解法:

   设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价 。 最小区间F[i,i]=0(一个数字无法合并,∴代价为0)每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段

区间DP模板,代码:

for(intp = 1 ; p <= n ; p++){      //p是区间的长度,作为阶段
for(int i = 1 ; i <= n ; i++){ //i是穷举区间的起点
int j = i+p-1; //j为区间的终点
for(int k = i ; k < j ; k++)//状态转移
dp[i][j] = min{dp[i][k]+dp[k+1][j]+w[i][j]};//这个是看题目意思,有的是要从k開始不是k+1
dp[i][j]= max{dp[i][k]+dp[k+1][j]+w[i][j]};
}
}

改题解法:

   对于这一题,假设我们仅仅对最左边的分割点到最右边的分割点进行DP。那么得到的答案肯定是错的。由于不是整个区间。所以我们必须在木棍的做左边和左右边分别添加一个点,那么得到的就是整个区间。再对这个区间进行DP求解就可以。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std; const int MAXN = 50 + 10;
const int INF = ~0U >> 2;
int w[MAXN],dp[MAXN][MAXN];
int L,n; int solve(){
n++;
for(int i = 0;i <= n;++i)
for(int j = i+1;j <= n;++j)
dp[i][j] = (i+1 == j ? 0 : INF); w[0] = 0; w[n] = L;
for(int p = 1;p <= n;++p){ //区间长度
for(int s = 0;s <= n-p;++s){ // 起始位置
int e = s + p; //终点
for(int k = s+1;k < e;++k){ //状态转移
dp[s][e] = min(dp[s][e],dp[s][k] + dp[k][e] + w[e] - w[s]);
}
}
}
return dp[0][n];
}
int main()
{
while(scanf("%d",&L),L){
scanf("%d",&n);
for(int i = 1; i <= n; ++i){
scanf("%d",&w[i]);
}
printf("The minimum cutting is %d.\n",solve());
}
return 0;
}

POJ的这题呢,能够看作是上体的上级版。

是没有给出固定的位置的,木板的分割顺序不确定,自由度非常高。这题貌似非常难入手。

可是事实上能够用稍微奇特的贪心来求解。

原理解析:

过程分析是一个涉及了哈夫曼编码的二叉树,由于分析过程有点下复杂。所以自己联想一下吧(囧)。以下给出的算法是不断求解huffman编码中的最小值和次小值。因此,朴素的算法是O(N*N).我们也能够想到用到二叉树结构的优先队列来求解最小值和次小值。此时的时间复杂度减为了O(NlogN).

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std; typedef long long LL;
const int MAXN = 20000 + 10; int N,L[MAXN]; LL solve(){
LL ans = 0; //直到计算到一块木板时为止
while(N > 1){
//寻找最小值,次小值
int mii1 = 0,mii2 = 1;
if(L[mii1] > L[mii2]) swap(mii1,mii2);
for(int i = 2; i < N; ++i){
if(L[i] < L[mii1]){
mii2 = mii1;
mii1 = i;
}
else if(L[i] < L[mii2]){
mii2 = i;
}
}
int t = L[mii1] + L[mii2]; //合并
ans += t; if(mii1 == N-1) swap(mii1,mii2);
L[mii1] = t;
L[mii2] = L[N-1];
N--;
}
return ans;
}
int main()
{
scanf("%d",&N);
for(int i = 0;i < N;++i){
scanf("%d",&L[i]);
}
printf("%lld\n",solve());
return 0;
}

使用STL中的priority_queue实现。

时间复杂度为O(N*logN).

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std; typedef long long LL;
const int MAXN = 20000 + 10; int N,L[MAXN]; LL solve(){
LL ans = 0; priority_queue<int,vector<int>,greater<int> > que;
for(int i = 0;i < N;++i){
que.push(L[i]);
} while(que.size() > 1){
int l1,l2;
l1 = que.top();
que.pop();
l2 = que.top();
que.pop(); ans += l1 + l2;
que.push(l1+l2);
}
return ans;
}
int main()
{
scanf("%d",&N);
for(int i = 0;i < N;++i){
scanf("%d",&L[i]);
}
printf("%lld\n",solve());
return 0;
}

使用手写堆实现。时间复杂度O(N*logN)。

堆实现模板:

//堆的实现
int heap[MAXN << 2],sz = 0 ; void push(int x){
//自己结点的编号
int i = sz++;
while(i > 0){
//父亲结点的编号
int p = (i - 1) >> 1; //假设已经没有大小颠倒则退出
if(heap[p] <= x)break; //把父亲结点的数值放下来。而把自己提上去
heap[i] = heap[p];
i = p;
}
heap[i] = x;
} int pop(){
//最小值
int ret = heap[0]; //要提到根的数值
int x = heap[--sz]; //从根開始向下交换
int i = 0;
while((i << 1 | 1) < sz){
//比較儿子
int a = i << 1 | 1,b = (i << 1) + 2;
if(b < sz && heap[b] < heap[a]) a = b; // 假设没有大小颠倒则退出
if(heap[a] >= x) break; //把儿子提上来
heap[i] = heap[a];
i = a;
} heap[i] = x;
return ret;
}



区间DP与贪心算法的联系(uav Cutting Sticks &amp;&amp; poj Fence Repair(堆的手工实现))的更多相关文章

  1. nyoj891(区间上的贪心)

    题目意思: 给一些闭区间,求最少须要多少点,使得每一个区间至少一个点. http://acm.nyist.net/JudgeOnline/problem.php?pid=891 例子输入 4 1 5 ...

  2. 基于贪心算法的几类区间覆盖问题 nyoj 12喷水装置(二) nyoj 14会场安排问题

    1)区间完全覆盖问题 问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖 样例: 区间长度8,可选的覆盖线段[2,6],[1, ...

  3. 算法提高 矩阵乘法 区间DP

    这是神题,n <= 1000,如果是极限数据普通的n^3区间DP怎么可能过?可偏偏就过了. 刘汝佳大哥的训练指南上面说的存在nlgn的算法解决矩阵链乘问题,可是百度都找不到.... AC代码 # ...

  4. 贪心算法----区间选点问题(POJ1201)

    题目: 题目的大致意思是,给定n个闭区间,并且这个闭区间上的点都是整数,现在要求你使用最少的点来覆盖这些区间并且每个区间的覆盖的点的数量满足输入的要求点覆盖区间的数量. 输入: 第一行输入n,代表n个 ...

  5. 【贪心算法】POJ-1328 区间问题

    一.题目 Description Assume the coasting is an infinite straight line. Land is in one side of coasting, ...

  6. 蓝桥 ADV-232 算法提高 矩阵乘法 【区间DP】

      算法提高 矩阵乘法   时间限制:3.0s   内存限制:256.0MB      问题描述 有n个矩阵,大小分别为a0*a1, a1*a2, a2*a3, ..., a[n-1]*a[n],现要 ...

  7. 洛谷 P1220 关路灯 (贪心+区间dp)

    这一道题我一直在想时间该怎么算. 看题解发现有个隐藏的贪心. 路径一定是左右扩展的,左右端点最多加+1(我竟然没发现!!) 这个性质非常重要!! 因此这道题用区间dp f[i][j]表示关完i到j的路 ...

  8. 51 nod 石子归并 + v2 + v3(区间dp,区间dp+平行四边形优化,GarsiaWachs算法)

    题意:就是求石子归并. 题解:当范围在100左右是可以之间简单的区间dp,如果范围在1000左右就要考虑用平行四边形优化. 就是多加一个p[i][j]表示在i到j内的取最优解的位置k,注意能使用平行四 ...

  9. 【贪心算法】POJ-2376 区间问题

    一.题目 Description Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cle ...

随机推荐

  1. 【php】 布尔值判断

    当转换为 boolean 时,以下值被认为是 FALSE: 布尔值 FALSE 本身 整型值 0(零) 浮点型值 0.0(零) 空字符串,以及字符串 "0" 不包括任何元素的数组 ...

  2. python计算机基础(一)

    什么是编程语言? 跟计算机交流的语言 什么是编程? 编程就是写代码,让计算机能够听懂的语言 为什么要编程? 让计算机为我们做事,取代人 计算机5大组成分别有什么作用? CPU:控制,判断,配作用,内存 ...

  3. JavaScript正则表达式-反向引用

    使用括号“()”进行分组,使子表达式(子模式)可以作为整体独立被修饰,子表达式所匹配的结果会被记录下来并可以单独被访问. /(a(b(cd){2})+)EF/ 则各引用分别对应: \1  对应(a(b ...

  4. Java中IO流讲解(一)

    一.概念 IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的类都在IO包中 流按流向分为两种:输入流,输出流 流按操作类型分为两种: 字节流 : 字节流可以操作 ...

  5. PAT Basic 1055

    1055 集体照 拍集体照时队形很重要,这里对给定的 N 个人 K 排的队形设计排队规则如下: 每排人数为 N/K(向下取整),多出来的人全部站在最后一排: 后排所有人的个子都不比前排任何人矮: 每排 ...

  6. numpy array_split()

    numpy.array_split(ary, indices_or_sections, axis=0)[source] Split an array into multiple sub-arrays. ...

  7. 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序

    题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...

  8. 【Luogu】P3455Zip-Queries(莫比乌斯反演)

    题目链接 真是神TM莫比乌斯 首先来看一个神奇的结论:求gcd(x,y)==k的对数,其中1<=x<=n,1<=y<=m 等同于求gcd(x,y)==1的对数,其中1<= ...

  9. 【Luogu】P2220容易题(快速幂)

    这题真是“容易”.呵呵呵. 参考题解:xyz32768 代码 #include<cstdio> #include<map> #include<algorithm> ...

  10. 洛谷P3588 - [POI2015]Pustynia

    Portal Description 给定一个长度为\(n(n\leq10^5)\)的正整数序列\(\{a_n\}\),每个数都在\([1,10^9]\)范围内,告诉你其中\(s\)个数,并给出\(m ...