SGU 183 Painting the balls (优化的动态规划)

题意:给n个白球,选其中一些涂为黑色,且给了涂第i个球的花费为ci,要求每m个连续的球中至少有两个黑球,问最小花费是多少?

容易想到一个方程dp[i][j]=min{dp[k][i]}+c[j]

dp[i][j]表示最后上色的两个球分别是第i和第j个球(i<j)时的最优解。

我们从条件每m个连续的球中至少有两个黑球可以得出k的范围j-m<=k<i

如果我们直接按上面这个方程去做的话时间复杂度是O(n*m*m)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define ll long long
#define mod 113
#define INF 100000000
using namespace std;
int c[10005];
int dp[205][105];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i) scanf("%d",&c[i]);
memset(dp,0x7f,sizeof(dp));
for(int i=2; i<=m; ++i)
for(int j=1; j<i; ++j)
dp[i%mod][i-j]=c[i]+c[j];
for(int i=m+1; i<=n; ++i)
for(int j=i-m+1; j<i; ++j)
{
int minn=INF;
for(int k=i-m; k<j&&i-k<=m; ++k)
minn=min(minn,dp[j%mod][j-k]);
dp[i%mod][i-j]=minn+c[i];
}
int ans=INF;
for(int i=n; i>=n-m+1; --i)
for(int j=i-1; i-j<=m&&j>=n-m+1; --j)
ans=min(ans,dp[i%mod][i-j]);

printf("%d\n",ans);
return 0;
}

但如果数据范围再大一点,就不行了。
所以我们需要优化一下转移部分。

对于上面那个方程而言,对于相同i,k的右边界是不变的,左边界随着j的变大的而减小。所以我们可以试着从大到小枚举j,这样k的区间就从小变大了。我们可以利用上一个区间的结果,从而降低转移的时间复杂度。

从另一种角度来阐述:

写一下dp[i][j+1]=min{dp[k][i]}+c[j+1],这里k的范围是j-m+1<=k<i。

我们发现这里k的范围是和dp[i][j]那个方程里面的k的范围是有重合的,准确说是dp[k][i]是有重合的。我们假设g=min{dp[k][i]},(j-m+1<=k<i)。这样求dp[i][j]=min{dp[k][i]}+c[j](j-m<=k<i)时,就变成了dp[i][j]=min{dp[j-m][i],g}+c[j]。这样我们只需要以i为阶段,从大到小枚举j就可以把转移这部分的时间复杂度降维O(1)。

这里的计算只适合j大于m的情况,所以需要预处理m以内的部分:dp[i][j]=c[i]+c[j](1<=i<j<=m)。

最终的答案需要从[n-m+1,n]之间的范围之内枚举。

由于卡内存所以要用滚动数组。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define ll long long
#define mod 101
using namespace std;
int dp[205][205];
int a[10005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
scanf("%d",&a[i]);
for(int i=1; i<=m; ++i)
for(int j=i+1; j<=m; ++j)
dp[i][j]=a[i]+a[j];
for(int i=1; i<=n; ++i)
{
int minn=0x7fffffff;
for(int j=min(i+m-1,n); j>max(i,m)&&j-i<m; --j)
{
minn=min(minn,dp[(j-m+mod)%mod][i%mod]);
dp[i%mod][j%mod]=minn+a[j];
}
}
int ans=0x7fffffff;
for(int i=n; i>=n-m+1; --i)
for(int j=i-1; i-j<m&&j>=n-m+1; --j)
ans=min(ans,dp[j%mod][i%mod]);
printf("%d\n",ans);
return 0;
}

View
Code

参考:http://wenku.baidu.com/view/89bd5f1e650e52ea551898fb.html

时间: 2024-07-31 12:21:06

SGU 183 Painting the balls (优化的动态规划)的相关文章

SGU 183.Painting the balls

时间限制:0.25s 空间限制:4M 题意:  在n(n<=10000)个球中,给若干个球涂色,每个球涂色的代价为Ci,使得任意连续m(m<=100)个球中有至少两个球被涂了色. Solution: 首先很直接地能想到一个DP的状态转移方程 f[i][j] 代表,当前涂第i个球,且前面最近一个被涂色的球为j的最小代价 f[i][j]=min(f[j][k])+Ci,   k>i+1-m 分析这个转移方程的时间复杂度是O(n*m*m)在此题的数据范围中高达10^8 显然我们需要更好的解法

Vijos 1243 生产产品 (优先队列优化的动态规划)

题意:中文题.不说了. 注意一些地方,机器的执行过程是没有顺序的,而且每个机器可以用多次.第一次执行的机器不消耗转移时间K. 用dp[i][j]表示第i个机器完成第j个步骤的最短时间,sum[j][i]表示第i个机器完成前j个步骤的时间. 比较容易想到一个朴素的状态转移方程: dp[i][j]=min{dp[k][j']+sum[j][i]-sum[j'][i]}+K  (j-j'<l),(i!=k) 这里状态是O(n*m),转移是O(n*l),一定会超时,需要优化. 方程变形得dp[i][j]

动态规划 知识汇总

 Dp状态设计与方程总结 不完全状态记录 <1>青蛙过河问题 <2>利用区间 dp 背包类问题 <1> 0-1 背包,经典问题 <2>无限背包,经典问题 <3>判定性背包问题 <4>带附属关系的背包问题 <5> + -1 背包问题 <6>双背包求最优值 <7>构造三角形问题 <8>带上下界限制的背包问题(012背包) 线性的动态规划问题 <1>积木游戏问题 <2&g

nyist oj 289 苹果 (动态规划——背包问题)

苹果 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 ctest有n个苹果,要将它放入容量为v的背包.给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值. 输入 有多组测试数据,每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v,n.v同时为0时结束测试,此时不输出.接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w.所有输入数字的范围大于等于0,小于等于1000. 输出 对每组测试数据输出一个整数,代表能放入背包的苹

01背包问题-动态规划算法

转 https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html 一.问题描述:有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和? 二.总体思路:根据动态规划解题步骤(问题抽象化.建立模型.寻找约束条件.判断是否满足最优性原理.找大问题与小问题的递推关系式.填表.寻找解组成)找出01背包问题的最优解以及解组成,然后编写代码实现: 三.动态规划的原理及过程: eg:number=4,ca

动态规划与贪婪算法

观点描述来源与博客:http://blog.csdn.net/woaimeinuo/article/details/45651163 贪婪:每一次都选择当前最好的,虽然不是全局最优解,但其可以让你找到局部最优解或是次优解.其实,有次优解也不错了.贪婪算法基本上是一种急功近利的算法,但是并不代表这种算法不好,如果贪婪的是一种长远和持续,又未尝不可呢? 动态规划: 对于大部分的问题,贪婪法通常都不能找出最优解,因为他们一般没有测试所有可能的解.因为贪婪算法是一种短视的行为,只会跟据当前的形式做判断,

01背包动态规划

有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和? 动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果.但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填

多重背包问题的二进制优化

算法:二进制优化,动态规划 #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N=2010; int v[N],w[N],cnt; int f[N]; int main(void){ int n, m; cin>>n>>m; for(int i=1,a,b,s;i<=n;i++){ cin>>a>&

动态规划——01背包

转 https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html 问题描述:有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和? 三.动态规划的原理及过程: eg:number=4,capacity=8 i 1 2 3 4 w(体积) 2 3 4 5 v(价值) 3 4 5 6       定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值:   寻找递推关系式,面对当