区间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= 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公式很简单。。。就是几何计算坑!!!

你可能感兴趣的:(区间DP)