洛谷 P1270 “访问”美术馆

思路

存图用时一个晚上,树形dp用时一个上午

首先我们这道题的存图可谓是读入紫题难度。。。看大佬们的存图好短为啥我的就这么长呢

这道题,我们假设走廊用时为边权。由于这是一棵树,我们走的时候是只能有唯一路径的,也就是你怎么进来的怎么出去,每条路一定要走两边,存图的时候便可以直接把路径长度开 2 2 2

找到有画的房间后,假设房间有 n n n幅画,因为搬一个走需要 5 s 5s 5s,这个可以转化成在这个房间面前有 n n n条走廊,每个长度为 5 5 5,连接到 n n n个点,每个点权值为1

紫题的存图就是这样,然后我们把边权视为时间,点权视为价值,跑一个树形dp分组背包就好了

设dp[u][j]表示以 u u u为根节点,用共计 j j j时间获得的最大价值,对于 u u u的每条出边 ( u , v ) (u,v) (u,v),有状态转移方程

d p [ u ] [ j ] = m a x ( d p [ u ] [ j ] , d p [ v ] [ k ] + d p [ u ] [ j − k − ( u , v ) ] ) dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][j-k-(u,v)]) dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][jk(u,v)])

其中

d p [ u ] [ 0 ] = 0 , e d g e [ i ] . d i s ≤ j ≤ s , 0 ≤ k ≤ j − ( u , v ) dp[u][0]=0,edge[i].dis\leq j\leq s,0\leq k\leq j-(u,v) dp[u][0]=0,edge[i].disjs,0kj(u,v)

d p dp dp精髓就式子和边界,我还有什么可讲的么 q w q qwq qwq

后面再来看这道题,感觉题解区有点不对劲,一个房间如果必定取完所有的画的话也能A了,然后又搞了一堆玄学的操作还是能A,于是我自己写了一个新题目,数据经过加强了qwq,题解见主页SOI文集的round0

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int orz=500100;
struct EDGE{
    int ver,dis,nxt,pre,num;
}edge[orz];
int head[orz],n,m,cnt,size[orz],v=2;
int dis[orz],s,l,r,f[5001][1001],ans;
inline int read(){
    int sym=0,res=0;char ch=0;
    while (ch<'0'||ch>'9')sym|=(ch=='-'),ch=getchar();
    while (ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return sym?-res:res;
}
void file(){
    freopen("read.in","r",stdin);
    freopen("write.out","w",stdout);
}
void add(int u,int v,int t){
    edge[++cnt].ver=v;
    edge[cnt].nxt=head[u];
    edge[cnt].pre=u;
    edge[cnt].dis=t;
    head[u]=cnt;
}
void dfs(int u){//可怕的存图
    int t=read()<<1,tmp=read();v++;add(u,v,t);
    if (tmp){
        for (int i=1;i<=tmp;i++){
            add(v,v+i,5);dis[v+i]=1;
        }v+=tmp;
    }else{
        dfs(v);
    }
    t=read()<<1;v++;tmp=read();add(u,v,t);
    if (tmp){
        for (int i=1;i<=tmp;i++){
            add(v,v+i,5);dis[v+i]=1;
        }v+=tmp;
    }else{
        dfs(v);
    }
}
void dp(int u){
    f[u][0]=dis[u];
    for (int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].ver;dp(v);
        for (int j=s;j>=edge[i].dis;j--){
            for (int k=j-edge[i].dis;k>=0;k--){
                f[u][j]=max(f[u][j],f[v][k]+f[u][j-k-edge[i].dis]);
            }
        }
    }
}
int main(){file();
    s=read()-1;
    int t=read()<<1;dis[2]=read();add(1,2,t);
    if (!dis[2])dfs(2);dp(1);
    for (int i=1;i<=s;i++){
        ans=max(ans,f[1][i]);
    }
    printf("%d",f[1][s]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值