acwing提高课区间dp

环形石子合并(环形)

在这里插入图片描述
样例输入:

4
4 5 9 4

样例输出:

43
54

代码模板:

//迭代式
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 410, INF = 0x3f3f3f3f;

int n;
int w[N], s[N];
int f[N][N], g[N][N];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> w[i];
        w[i + n] = w[i];
    }

    for (int i = 1; i <= n * 2; i ++ ) s[i] = s[i - 1] + w[i];

    memset(f, 0x3f, sizeof f);
    memset(g, -0x3f, sizeof g);
 
    for (int len = 1; len <= n; len ++ )  //长度
        for (int l = 1; l + len - 1 <= n * 2; l ++ ) //左端点
        {
            int r = l + len - 1;
            if (l == r) f[l][r] = g[l][r] = 0;
            else
            {
                for (int k = l; k < r; k ++ )
                {
                    f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
                    g[l][r] = max(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]);
                }
            }
        }

    int minv = INF, maxv = -INF;
    for (int i = 1; i <= n; i ++ )
    {
        minv = min(minv, f[i][i + n - 1]);
        maxv = max(maxv, g[i][i + n - 1]);
    }

    cout << minv << endl << maxv << endl;

    return 0;
}

 

能量项链(环形DP)

在这里插入图片描述
样例输入:

4
2 3 5 10

样例输出:

710

代码模板:

#include<cstdio>
#include<iostream>
using namespace std;

const int N = 210;//将n的环化为2n的链

int n;
int w[N];
int f[N][N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> w[i];
        w[i + n] = w[i];//依次copy前面的
    }

    //枚举区间长度,区间最短为3,因为最终是合成长度为2的区间。
    for (int len = 3; len <= n + 1; ++len) {
        //枚举左端点
        for (int l = 1; l + len - 1 <= n + n; ++l) {
            int r = l + len - 1;//右端点
            //枚举分界线
            for (int k = l + 1; k < r; ++k) {
                f[l][r] = max(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
            }
        }
    }

    int ans = 0;
    for (int l = 1; l <= n; l++) {
        ans = max(ans, f[l][l + n]);
    }

    cout << ans << endl;
    return 0;
}

 

凸多边形的划分

在这里插入图片描述
思路:抽象成区间dp是一个非常重要的过程
在这里插入图片描述

样例输入:

5
121 122 123 245 231

样例输出:

12214884

代码模板:

//未加高精度 
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 55,INF = 1e9;
int n,w[N];
int f[N][N];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i];
		w[i+n]=w[i];
	}
	
	for(int len=3;len<=n;len++)
		for(int l=1;l+len-1<=n;l++)
		{
			int r=l+len-1;
			f[l][r]=INF;
			for(int k=l+1;k<r;k++)
				f[l][r]=min(f[l][r],f[l][k]+f[k][r]+w[l]*w[k]*w[r]); 
				cout<<l<<" "<<r<<" "<<f[l][r]<<endl;
		}
		
	cout<<f[1][n]<<endl;
					
	return 0;
	
} 

 

加分二叉树

在这里插入图片描述
样例输入:

5
5 7 1 2 10

样例输出:

145
3 1 2 4 5

代码模板:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 50;

int n;
int w[N];
unsigned f[N][N];
int root[N][N];

void dfs(int l, int r)
{
    if (l > r) return;

    int k = root[l][r];
    printf("%d ", k);
    dfs(l, k - 1);
    dfs(k + 1, r);
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    for (int len = 1; len <= n; len ++ )
        for (int l = 1; l + len - 1 <= n; l ++ )
        {
            int r = l + len - 1;

            for (int k = l; k <= r; k ++ )//遍历根节点的位置
            {
                int left = k == l ? 1 : f[l][k - 1];
                int right = k == r ? 1 : f[k + 1][r];
                int score = left * right + w[k];
                if (l == r) score = w[k];
                if (f[l][r] < score)
                {
                    f[l][r] = score;
                    root[l][r] = k;
                }
            }
        }

    printf("%d\n", f[1][n]);
    dfs(1, n);
    puts("");

    return 0;
}

 

棋盘分割(记忆式搜索)

在这里插入图片描述

样例输入:

3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3

样例输出:

1.633

代码模板:

//记忆式搜索
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 15, M = 9;
const double INF = 1e9;

int n, m = 8;
int s[M][M];
double f[M][M][M][M][N];//将矩阵(x1,y1)(x2,y2)分为k部分的方案的最小值
double X;

int get_sum(int x1, int y1, int x2, int y2) //矩阵和
{
    return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}

double get(int x1, int y1, int x2, int y2)//上述公式
{
    double sum = get_sum(x1, y1, x2, y2) - X;
    return (double)sum * sum / n;
}

double dp(int x1, int y1, int x2, int y2, int k)
{
    double &v = f[x1][y1][x2][y2][k];
    if (v >= 0) return v;
    if (k == 1) return v = get(x1, y1, x2, y2);

    v = INF;
    for (int i = x1; i < x2; i ++ )//分割分成两部份,分别dp
    {
        v = min(v, get(x1, y1, i, y2) + dp(i + 1, y1, x2, y2, k - 1));
        v = min(v, get(i + 1, y1, x2, y2) + dp(x1, y1, i, y2, k - 1));
    }

    for (int i = y1; i < y2; i ++ )//同
    {
        v = min(v, get(x1, y1, x2, i) + dp(x1, i + 1, x2, y2, k - 1));
        v = min(v, get(x1, i + 1, x2, y2) + dp(x1, y1, x2, i, k - 1));
    }

    return v;
}

int main()
{
    cin >> n;
    for (int i = 1; i <= m; i ++ )
        for (int j = 1; j <= m; j ++ )
        {
            cin >> s[i][j];
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
        }

    X = (double)s[m][m] / n;
    memset(f, -1, sizeof f);
    printf("%.3lf\n", sqrt(dp(1, 1, 8, 8, n)));

    return 0;
}

 

posted @ 2022-03-22 14:38  panse·  阅读(23)  评论(0)    收藏  举报