当前位置: 首页 > article >正文

fwdiary(8) 区间dp,树形dp 记忆化搜索

1.划分大理石(背包问题)

 多重背包问题,本题数据用二进制优化即可。

从6种大理石,每种有ai个,恰好选出重量为j,最终答案是 重量为总重/2

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

using namespace std;

const int N = 60010;

bool f[N];
int a[10];

int main()
{
    while(true)
    {
        int m = 0;
        for (int i = 1; i <= 6; i ++ )
        {
            cin >> a[i];
            m += a[i] * i;
        }
        
        if(!m) break;
        
        if(m&1) puts("Can't");
        else
        {
            m/=2;
            memset(f,0,sizeof f);
            f[0]=true;
            for(int i=1;i<=6;i++)
            {
                int s=a[i],k=1;
                while(s>=k)
                {
                    for(int j=m;j>=i*k;j--) f[j]|=f[j-i*k];
                    s-=k;
                    k*=2;
                }
                if(s) for(int j=m;j>=i*s;j--) f[j]|=f[j-i*s];
            }
            if(f[m]) puts("Can");
            else puts("Can't");
        }
    }
    return 0;
}

2.棋盘分割

321. 棋盘分割 - AcWing题库

状态表示显然为,一个矩形区域四个坐标,加切割成多少块。

枚举一次切割的位置即可。注意切割之后,还有两种情况。假设切割成A,B两部分,一种是把A作为单独切出来的一块,然后继续切割B。另一种反之。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

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

double X;
int n,m=8;
int s[M][M];
double f[M][M][M][M][M];


int get_sum(int x1,int y1,int x2,int y2)
{
    return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+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 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 get(x1,y1,x2,y2);
    v=INF;
    
    for(int i=x1;i<x2;i++)
    {
        v=min(dp(x1,y1,i,y2,k-1)+get(i+1,y1,x2,y2),v);
        v=min(dp(i+1,y1,x2,y2,k-1)+get(x1,y1,i,y2),v);
    }
    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,m,m,n)));
    return 0;
}

3.消木块 区间dp,,多加一维

322. 消木块 - AcWing题库

如果只有l,r。那么再r之后,在l之前同时消去的方案可能记录不到,写不出状态转移方程。

因此先考虑多加一维,在r之后还连续消掉了co个块。

因此状态划分枚举在l,r内,如果wk==wr,则把k+1到r的消去,wk接上r之后的co个一起消去

枚举初值是把r消去,也就是l到r-1消去,再加(1+co)平方

f(l,r,co)=max\left \{ f(l,k,1+co)+f(k+1,r-1,0) \right \}

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N =210;

int f[N][N][N];
int n;
int a[N];

int dp(int l,int r,int co)
{
    int &v=f[l][r][co];
    if(v>=0) return v;
    
    if(l>r) return 0;
    v=dp(l,r-1,0)+(1+co)*(1+co);
    for(int k=l;k<r;k++)
    {
        if(a[k]==a[r])
        {
            v=max(v,dp(l,k,1+co)+dp(k+1,r-1,0));
        }
    }
    return v;
}

int main()
{
    int T;
    cin>>T;
    for(int cases=1;cases<=T;cases++)
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        memset(f,-1,sizeof f);
        
        printf("Case %d: %d\n",cases,dp(1,n,0));
    }
}

4.贿赂FIPA (树形dp+背包问题+记忆化搜索)

324. 贿赂FIPA - AcWing题库

选一个根节点就把整个子树都选了。那就状态表示,在以u为根的子树中选择,得到超过j张票的最小花费。

那么必要的信息就要子树中包含节点的个数。假设我们已经知道了这个size数组,来思考状态转移方程。首先遍历节点u的所有节点,枚举在该节点获得多少张票。此时我们应该知道,以子节点为根的子树中,选择k张票的最小代价,将这些看做物品(在子节点为根的子树中选择0到size(j)张票),那么实际上是个01背包问题

因此可解。最后不要忘记选择u本身的答案。

#include<iostream>
#include<algorithm>
#include<cstring>
#include <bits/stdc++.h>
using namespace std;

//以u为根的子树,获得j票的最小花费
const int N=210,M=2*N;
string s, a, b;
stringstream ss;

int n, m;
int val[N];
map<string, int> mp;int cnt;
int father[N];

int h[N], e[M], ne[M], idx;
int f[N][N], sz[N];

int fd(string s)
{
    return mp.find(s) == mp.end() ? mp[s] = ++ cnt : mp[s];
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dp(int u)
{
    f[u][0]=0;
    sz[u]=1;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==father[u]) continue;
        
        dp(j);
        sz[u]+=sz[j];
    }
    for(int i=h[u];~i;i=ne[i])//从子树中选择
    {
        int ver=e[i];
        if (ver == father[u]) continue;
        for(int j=sz[u];j;j--)
        {
            for(int k=sz[ver];k;k--)
                if(j>=k)
                    f[u][j]=min(f[u][j],f[u][j-k]+f[ver][k]);
        }
    }
    if(u)//选择自身
        for (int i = 1; i <= sz[u]; i ++ ) f[u][i] = min(f[u][i], val[u]);
}

int main()
{
    while (getline(cin, s) && s[0] != '#')
    {
        cnt=0;
        mp.clear(), cnt = idx = 0;
        memset(h, -1, sizeof h);
        memset(father, 0, sizeof father);
        memset(f, 0x3f, sizeof f);
        ss.clear(), ss.str(s); ss >> n >> m;
        for (int i = 1, u, v; i <= n; i ++ )
        {
            getline(cin, s);
            ss.clear(), ss.str(s); ss >> a;
            u = fd(a), ss >> val[u];
            while (ss >> b)
            {
                v = fd(b);
                add(u, v), add(v, u);
                father[v] = u;
            }
        }
        for (int i = 1; i <= n; i ++ )
            if (father[i] == 0)
                add(i, 0), add(0, i);
        dp(0);
        printf("%d\n", f[0][m]);

    }
    
}

5.计算机(树形dp  树的中心)

325. 计算机 - AcWing题库

某一点距离该点最远距离,就是往下最深距离,和网上最深距离中更深的一个;

往下最深距离简单,不说了、

往上最深距离就是:如果当前节点不是根节点往下d1所在分支,那么就是max(up[u],d1[u])

如果是,就是和d1[u]取最大值。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N =10010,M=2*N;
const int INF=0x3f3f3f3f;

int n,m;
int h[N],e[M],ne[M],w[M],idx;
int d1[N],d2[N],up[N];
int p[N];
bool isleaf[N];


int res[N];


void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dfs_d(int u,int father)
{
    d1[u]=d2[u]=-INF;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==father) continue;
        int d=dfs_d(j,u)+w[i];
        if(d>=d1[u])
        {
            d2[u]=d1[u];
            d1[u]=d;
            p[u]=j;
        }
        else if(d>d2[u]) d2[u]=d;
    }
    if(d1[u]==-INF)
    {
        d1[u]=d2[u]=0;
        isleaf[u]=true;
    }
    return d1[u];
}

void dfs_u(int u,int father)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==father) continue;
        if(p[u]==j)  up[j]=max(up[u],d2[u])+w[i];
        else up[j]=max(up[u],d1[u])+w[i];
        dfs_u(j,u);
    }
    
}


void  solve(int n)
{
    memset(h,-1,sizeof h);
    idx=0;memset(isleaf,0,sizeof isleaf);
    for(int i=2;i<=n;i++)
    {
        int a,c;
        cin>>a>>c;
        add(a,i,c),add(i,a,c);
    }
    dfs_d(1,-1);
    dfs_u(1,-1);
    
    for(int i=1;i<=n;i++)
    {
        if(isleaf[i]) res[i]=up[i];
        else res[i]=max(up[i],d1[i]);
        
        cout<<res[i]<<endl;
    }
    
}

int main()
{int n;
    while(cin>>n)
    {
        solve(n);
    }
}


http://www.kler.cn/a/4533.html

相关文章:

  • 【k8s】用户和服务账户联系(user、serviceaccount、sa)
  • 源码安装httpd2.4
  • 【Spring Boot 应用开发】-04-01 自动配置-数据源-连接池
  • 【某大型互联网企业】软件测试面试经验分享(1 ~ 3年)
  • .NET framework、Core和Standard都是什么?
  • 在 Ubuntu 上安装和配置 Redis
  • 2023年学习系列之读出计划
  • SpringBoot整合Flink(施耐德PLC物联网信息采集)
  • Wing IDE 解决鼠标悬浮
  • 人工智能、深度学习和机器学习有哪些区别?
  • 免费空间主机是什么?怎么申请免费空间主机
  • English Learning - L2 第 9 次小组纠音 辅音 [s] [z] [ʃ] [ʒ] [h] [ʧ] [ʤ] 2023.3.25 周六
  • NDK FFmpeg音视频播放器五
  • 深入学习JavaScript系列(三)——this
  • JWT基础教程
  • 注意力汇聚 笔记
  • IO进程线程-标准IO(结)
  • 探究C/C++ typedef的秘密
  • Mysql排序后分页 分页数据有重复
  • python条件语句与循环语句
  • Unity游戏崩溃日志查询笔记 安卓平台 关于tombstone_00
  • TCC真没这么简单,一文讲透|分布式事务系列(三)
  • 面试官常问的设计模式及常用框架中设计模式的使用(一)
  • 树莓派学习笔记(八)树莓派Linux内核开发准备工作及概念
  • Java基础 -- 关键字Static和Final
  • docker-compose部署rabbitmq集群