动规

来源:互联网 发布:mac os x 10.7 qq下载 编辑:程序博客网 时间:2024/04/28 16:42


Description

组长大人付了很多次旅游费用之后终于破产了,他决定从现在开始每周指定一个人付款,但是显然这样的事情谁也接受不了,所以组长提出来通过游戏来决定这一周是组长付钱还是被选中的人付钱。
游戏规则是这样的:首先不参与竞争的人随即写下若干个整数得到一个数列,每次可以取走一个数,所花费的代价就是这个数乘以他左边和右边的两个数。最左边和最右边的数不能取,取到最后一次的时候刚好剩下2个数字的时候停止。
当然这种简单的问题对于脑洞大开的组长大人实在是小菜一碟,他一眼就预计出了最小花费。
所以不参与竞争的人随即提出了第二个要求使得游戏变得公平,只由被选中的人执行上述操作,最后得到的花费如果小于等于组长预计的花费,那就算赢,组长付钱,否则就要悲催的付钱了。
这天轮到你来跟组长竞争,聪明的你能有办法保持不败吗?

Input

多组数据,每组数据两行,第一行一个整数n(<=100)表示数列的大小,第二行n个1-100的整数,代表数列。

Output

一个整数,最小花费。

Sample Input

6 10 1 50 50 20 5

Sample Output

3650

HINT


题解:

一个区间动规的基础题,没有接触过区间DP的可能会有点困难。取数的过程有一个典型的特点,就是取走一个数之后这个大区间就被分成了两个小区间,也就是说可以定义状态dp(i,j)表示从第i个数到第j个数能得到的最小分数,所以状态转移方程就是枚举这个区间间断点k,dp[i,j] = min(dp[i,k] + dp[k,j] + a[i] * a[k] * a[j])。这样根据这个递推式子递推一下就可以得到正确答案了。问题的分割、整合转移是区间动态规划的典型特点,而如何看出一个问题是区间动规则需要大家更多的接触区间动规的题目,掌握区间的真正含义,而不是仅仅停留在数学上的区间。

#include <iostream>#include <cstring>#include <cstdio> using namespace std; const int maxn = 0x3f3f3f3f; int n,a[101];int f[101][101]; int main() {    //freopen("in.txt","r",stdin);    while (cin>>n) {        for (int i = 1; i<=n; i++) cin>>a[i];        memset(f,maxn,sizeof(f));        for (int i = 1; i<=n; i++)            f[i][i-1] = f[i][i] = f[i][i+1] = 0;        for (int i = n-2; i>0; i--)            for (int j = i+2; j<=n; j++)                for (int k = i+1; k<j; k++)                    if (f[i][j]>f[i][k]+a[i]*a[k]*a[j]+f[k][j])                       f[i][j]=f[i][k]+a[i]*a[k]*a[j]+f[k][j];        cout<<f[1][n]<<endl;    }}



Description

如果说ACM组的周末在组长大人的带领下是天堂的话,那么周一到周五就是在组长大人的压迫下的地狱。每天组长大人都会给大家挂出来各种各样的练习赛。
你每天在组里的时间为n分钟,假设是从第一分钟开始,到第n分钟结束。如果在同一时刻有多场练习赛开始,那么你只需要选择一个来完成。反之,如果当前时刻只有一场比赛,那么你就必须去做。如果在某一时刻的时候你正在做一场练习赛的话,那么从这个时刻开始的练习赛你就不能做了。
练习赛必须从开始做到结束。当然聪明的你想在不被组长大人发现的情况下通过合理选择做哪些比赛来忙里偷闲。

Input

多组数据。
每组数据第一行为两个整数n,k(0< n,k <= 10000)表示在组里的时间和练习赛场次。
接下来k行每行两个整数p,t。p表示练习赛开始的时刻,t表示练习赛持续的时间。

Output

一个整数,表示这一天中你的最大空闲时间。

Sample Input

15 6 1 2 1 6 4 11 8 5 8 1 11 5

Sample Output

4


题解:

看问题,不难看出来是一个一维DP,但是问题就出在了如何转移的问题,不妨各种想法都试试,比如就定义dp(i)表示1-i分钟能有多少休息时间,而不难发现这样的方程是存在后效性的,因为第i分钟的工作状态是影响这个状态往后转移的。所以就换一种思想,定义dp(i)表示i-n分钟能休息的时间,可见这个状态是完全可行的,只需要判断是否有在第i分钟开始的工作即可,对于没有在第i分钟开始的情况,dp[i] = dp[i+1] + 1,而对于有开始的就要枚举在第i分钟开始的工作j,计算dp[i+t[j]]的最大值。当然我还是很仁慈的,数据里边的工作开始时间给出的顺序是按照递增顺序给出的,所以一个完美的O(n+k)的复杂度。

#include <cstdio>#include <iostream>#include <cstring> using namespace std; const int maxn = 10000 + 5;int n,k;int p[maxn],t[maxn],f[maxn]; int main() {    //freopen("in.txt","r",stdin);    while (cin>>n>>k) {        for (int i = 1; i <= k; i++) scanf("%d%d",&p[i],&t[i]);        int x = k;        memset(f,0,sizeof(0));        for (int i = n; i > 0; i--) {            if (i > p[x]) f[i] = f[i+1] + 1;            else {                f[i] = f[i+t[x]];                x--;                while (i == p[x]) {                    f[i] = max(f[i],f[i+t[x]]);                    x--;                }            }        }        cout<<f[1]<<endl;    }}



0 0
原创粉丝点击