DP刷题(1500-1700)

1.区间DP:https://www.acwing.com/problem/content/323/

比较容易想到区间DP,转换一下均方差定义用记忆化搜索就可以了。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 16;
int n, m = 8;
int s[N][N];
double f[N][N][N][N][N];
double X;
double get(int x1, int y1, int x2, int y2)
{
    double delta = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
    delta = delta - X;
    return delta * delta;
}
double dp(int k, int x1, int y1, int x2, int y2)
{
    if (f[k][x1][y1][x2][y2] >= 0) return f[k][x1][y1][x2][y2];
    if (k == n) return f[k][x1][y1][x2][y2] = get(x1, y1, x2, y2); 

    double t = 1e9; //初始化为无穷大
    for (int i = x1; i < x2; i ++ ) //横着切
    {
        t = min(t, dp(k + 1, x1, y1, i, y2) + get(i + 1, y1, x2, y2));
        t = min(t, dp(k + 1, i + 1, y1, x2, y2) + get(x1, y1, i, y2));
    }
    for (int i = y1; i < y2; i ++ ) //竖着切
    {
        t = min(t, dp(k + 1, x1, y1, x2, i) + get(x1, i + 1, x2, y2));
        t = min(t, dp(k + 1, x1, i + 1, x2, y2) + get(x1, y1, x2, i));
    }
    return f[k][x1][y1][x2][y2] = t;
}
int main()
{
    cin>>n;
    for (int i = 1; i <= m; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin>>s[i][j];
    for (int i = 1; i <= m; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    memset(f, -1, sizeof f);
    X = (double) s[m][m] / n;
    printf("%.3lf\n", sqrt(dp(1, 1, 1, m, m) / n));
    return 0;
}

2.树形/换根DP:https://www.acwing.com/problem/content/1075/

我们不妨以1为根建树。

对于任意一个点X,他的最长距离就是max(d1[x],up[x]),其中d1是它向下的最长链,up是它向上的最长链,对于d1,我们只需要用儿子节点更新父节点即可,关键在于up[x]的求法:

首先,我们找到x的父亲k,up[x]=w[x][k]+maxx:这里w[x][k]表示x与k的边权,maxx就是父亲的up[k]和父亲的向下子链的max,这里注意,向下子链不可以穿过x,于是我们用d2[x]维护向下链的次大值,当我们发现最大值穿过时用次大值更新。

所以我们需要知道最大子链是否穿过x,我们只要在第一遍dfs维护root选max时的儿子节点即可。

那么我们怎么实现呢?我们发现这其实是用父节点更新儿子节点的过程,再来一遍DFS即可。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
    int dian;
    int zhi;
};
int d1[100010],d2[100010],up[100010];
int s1[100010],s2[100010];
vector<node> edge[100010];
void dfs1(int root,int fa){
    d1[root]=d2[root]=0;
    for(int i=0;i<edge[root].size();i++){
        int x=edge[root][i].dian;
        if(fa==x) continue;
        dfs1(x,root);
        if(d1[x]+edge[root][i].zhi>d1[root]){
            d2[root]=d1[root];
            d1[root]=d1[x]+edge[root][i].zhi;
            s1[root]=x;
        }
        else if(d1[x]+edge[root][i].zhi>d2[root]){
            d2[root]=d1[x]+edge[root][i].zhi;
        }
    }
}
void dfs2(int root,int fa){

    for(int i=0;i<edge[root].size();i++){
        int x=edge[root][i].dian;
        if(fa==x) continue;
        //更新儿子
        up[x]=edge[root][i].zhi;
        if(s1[root]==x) up[x]+=max(up[root],d2[root]);
        else up[x]+=max(up[root],d1[root]);
        dfs2(x,root);
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int a,b,c;
        cin>>a>>b>>c;
        edge[a].push_back({b,c});
        edge[b].push_back({a,c});
    }
    dfs1(1,-1);
    dfs2(1,-1);
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++){
        ans=min(ans,max(up[i],d1[i]));
    }
    cout<<ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值