区间DP

区间DP,对于每段小区间,它的最优值是由更小的区间的最优值得出的,由此往下划分,直到单个元素,由他们的组合合并得出最优解。

484感觉这个有套路

首先要分析题目,找最优子区间,分清取左区间边界,还是右区间边界

总结了一个区间的套路板子,这个过程也可以用递归实现

for (int l = st; l < len-(); l++) //l:区间长度
    for (int i = st; l < len-l+1; l++) // i:区间起点
    {
        int j = i+l-1; //j: 区间终点
        for (int k = i; k < j; k++) // 状态转移过程
            dp[i][j] = MAX/MIN(f[i][j], f[i][k]+f[k+1][j]+w[i][j]);
            //f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] } 
            // 这个484很像最短路/最小生成树的松弛过程
    }

下面来看几个栗子吧
POJ1651(石子合并类)
大致题意就是给你一个数列,从非头尾的数字中取数字,求这些数字与左右数字乘积之和,求最小和。很明显的石子合并问题,求每个区间最小值,合并得出区间最小值。

#define LL long long
#define INF 1e9
const int N = 101;
LL dp[N][N], p[N];
int vis[N];

int main()
{
    int n;
    scanf("%d", &n);
    int minn=-INF, cur;
    for (int i = 1; i <= n; i++)
        scanf("%lld", &p[i]);
       
    for (int m = 2; m <= n-1; m++) //可取区间[2, n-1]
        for (int i = 2; i <= n-m+1; i++) //区间起点,第二个2数字
        {
            int j = m+i-1; //区间终点,最小组合区间长度3
            dp[i][j] = INF;
            for (int k = i; k < j; k++)
                dp[i][j] = min(dp[i][j], dp[i][k] + p[i-1]*p[j]*p[k] + dp[k+1][j]);
        }
    printf("%I64d\n", dp[2][n]);
}

POJ2955(括号合并问题)

const int  N = 120;
int dp[N][N];
string s;

int main()
{
    while(cin >> s)
    {
        if(s == "end") break;
        CLR(dp);
        int len = s.size();
        for(int i = 1; i < len; i++)
        {
            int k = 0;
            for(int j = i; j < len; j++)
            {
                if(s[k]=='('&&s[j]==')' || s[k]=='['&&s[j]==']')
                    dp[k][j] = dp[k+1][j-1] + 2;

                for(int l = k; l < j; l++)
                    dp[k][j] = max(dp[k][j], dp[k][l]+dp[l+1][j]);
                k++;
            }
        }
        printf("%d\n", dp[0][len-1]);
    }
    return 0;
}

hdu4283
大致意思就是导演想要知道怎么使男孩的失望值最低,因为题目说的是先入后出,所以要反过来枚举

#define CLR(x) memset(x, 0, sizeof(x))
const int N = 105;
int D[N], dp[N][N];
int sum[N];

int main()
{
    int T;
    scanf("%d", &T);
    int k = 1;
    while (T--)
    {
        CLR(D); CLR(dp); CLR(sum);
        sum[0] = 0;
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &D[i]);
            sum[i] = sum[i-1] + D[i]; // get所有区间的值
        }

        for (int i = n; i >= 1; i--)
            for (int j = i; j <= n; j++)
            {
                dp[i][j] = INF;
                for (int d = 1; d <= j-i+1; d++)
                {
                    int k = i+d-1;
                    dp[i][j] = min (dp[i][j], dp[i+1][k]+dp[k+1][j]+(d-1)*D[i]+d*(sum[j]-sum[k]));
                }
            }

        printf("Case #%d: %d\n", k++, dp[1][n]);
    }
}

hdu2476
这题题目有毒!!!!
阿西吧,读了两个小时没读懂,可能是我菜吧,最后查了题解才搞懂题目意思
分析一下样例,他题目中所谓的刷一下是这样的

0:zzzzzfzzzzz
1:aaaaaaaaaaa      [0,10]
2:abbbbbbbbba   [1,9]
3:abcccccccba      [2,8]
4:abcdddddba     [3,7]
5:abcdeeedba      [4,6]
6:abcdefedba       [5]

上代码

const int N = 105;
int dp[N][N];
char a[105], b[105];
int ans[105];

int main()
{
    while (scanf("%s%s", a, b) != EOF)
    {
        int len = strlen(a);
        for (int i = 0; i < len; i++)
        {
            for (int j = i; j >= 0; j--)
            {
                dp[j][i] = dp[j+1][i] + 1;
                for (int k = j+1; k <= i; k++)
                    if (b[j] == b[k])
                        dp[j][i] = min(dp[j][i], dp[j+1][k]+dp[k+1][i]);
            }
            ans[i] = dp[0][i];
        }
        for (int i = 0; i < len; i++)
        {
            if (a[i] == b[i]) ans[i] = ans[i-1];
            else
            {
                for (int j = 0; j < i; j++)
                    ans[i] = min(ans[i], ans[j]+dp[j+1][i]);
            }
        }
        printf("%d\n", ans[len-1]);
    }
}

下面就是笔者之前提过,真正有可能在比赛中出现的区间DP,带几何计算的。
ZOJ3537
大致题意:给你一堆的点(二维坐标的),问又他们构成的多边形能不能切成三角形,如果可以求出它的最小代价,对,每切一刀都是有代价的(costi, j = |xi + xj| * |yi + yj| % p. )
这是个凸包判断+三角剖分+区间DP的问题
GG上板子上板子

#define CLR(x) memset(x, 0, sizeof(x))
int dp[305][305], cost[305][305];
int n, P;
struct point
{
    double x, y;
} p[305], tmp[305];

bool mult(point st, point ed, point p)
{
    return (st.x-p.x)*(ed.y-p.y) >= (ed.x-p.x)*(st.y-p.y);
}
bool operator < (const point &a, const point &b)
{
    return a.y<b.y || (a.y==r.y && a.x<b.x);
}

int Graham(point pnt[], int n, point res[])
{
    sort(pnt, pnt + n);
    if (n == 0) return 0; res[0] = pnt[0];
    if (n == 1) return 1; res[1] = pnt[1];
    if (n == 2) return 2; res[2] = pnt[2];

    int top = 1;
    for (int i = 2; i < n; i++)
    {
        while (top && mult(pnt[i], res[top], res[top-1])) top--;
        res[++top] = pnt[i];
    }

    int len = top;
    for (int i = n - 3; i >= 0; i--)
    {
        while (top!=len && mult(pnt[i], res[top], res[top-1])) top--;
        res[++top] = pnt[i];
    }
    return top;
}

int cal(int i, int j)
{
    return abs((int)tmp[i].x+(int)tmp[j].x) * abs((int)tmp[i].y+(int)tmp[j].y) % P;
}

int main()
{
    while (scanf("%d%d", &n, &P) != EOF)
    {
        CLR(p); CLR(tmp); CLR(cost);
        for (int i = 0; i < n; i++)
            scanf("%lf%lf", &p[i].x, &p[i].y);
        if (n <= 3) printf("0\n");
        else if (Graham(p, n, tmp) < n) printf("I can't cut.\n");
        else
        {
            for (int i = 0; i < n; i++)
                for (int j = i+2; j < n; j++)
                    cost[i][j] = cost[j][i] = cal(i, j);

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                    dp[i][j] = INF;
                dp[i][(i+1)%n] = 0; //dp[i][i+1]=0(i!=n-1) dp[n-1][0]=0
            }

            for (int i = n-3; i >= 0; i--)
                for (int j = i+2; j < n; j++)
                    for (int k = i+1; k <= j; k++)
                        dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);
            printf("%d\n", dp[0][n-1]);
        }
    }
    return 0;
}

这题的区间DP公式很简单。。。就是几何计算坑!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容