[LOJ#10157]皇宫看守

题面

这里(〃'▽'〃)

题意

  大概就是有一棵n个节点的树,i号节点有一个费用wi。可以在某些点上安置侍卫监察,与该点相邻的点都视为安全。求使整棵树安全所需最小费用。

闲扯

  一眼秒出做法树形dp,接着想到用dp[0][i]表示i点无侍卫,dp[1][i]表示i点有侍卫来进行转移,然鹅此题监察的是点而不是边...GG
  于是需要额外考虑i点的状态。

分析

我们重新定义dp方程:
  - dp[0][u]表示u点有人且安全时整棵子树的最小费用;
  - dp[1][u]表示u点无人但安全时整棵子树的最小费用;
  - dp[2][u]表示u点无人且不安全时整棵子树的最小费用。
注意我们钦定上述条件建立在u的孩子都安全的基础上.
易得dp[0][u],dp[2][u]的转移:
  \(·dp[0][u]=\sum min\lbrace dp[0][v],dp[1][v],dp[2][v] \rbrace +w[u]\)
  \(·dp[2][u]=\sum dp[1][v]\)
  (v为u的儿子)
  解释:当u点有人时v点可以被监察,故可以是任何状态;当u点无人且不安全时v点不能有人,但必须安全。


于是本题最大的难点就在于dp[1][u]的转移:
  u点无人,但是安全说明u的儿子都安全且其中至少有一个是有人的。若不考虑u点的安全问题,方程是这样的:\(dp[1][u]=\sum min\lbrace dp[0][v],dp[1][v]\rbrace\)
  我们发现,当dp[0][v]<dp[1][v]时,v会将"有人"的状态转移给u,这样u就安全了;于是我们考虑当所有的dp[0][v]都大于dp[1][v]的时候,一定要找一个损失尽量少,即使dp[0][v]-dp[1][v]最小的那个v.

代码

具体见代码注释.

#include <iostream>
#include <cstdio>
#include <cctype>
#define il inline
#define vd void
#define rg register
#define rep(i,x,y) for(rg int i=x;i<=y;++i)
#define drp(i,x,y) for(rg int i=x;i>=y;--i)
using namespace std;
const int Len=2333333,mn=1505;
char buf[Len],*p1=buf,*p2=buf,duf[Len],*q1=duf;
il char gc(); il int rd(); il vd pc(char c); il vd rt(int x); il vd flush();
template<class T> il T Max(T a,T b){return a>b?a:b;}
template<class T> il T Min(T a,T b){return a<b?a:b;}
int n,u,v,cnt,m,w[mn],h[mn],fa[mn],dp[3][mn];
struct E{int to,nxt;}e[mn<<1];
il vd Add(int u,int v){e[++cnt]=(E){v,h[u]},h[u]=cnt;}
vd Dfs(int u){int fake=5e8,f=0;  //保险些的话极大值可以再小点;f为标记
    dp[0][u]=w[u]; //u点有人需花费
    for(rg int i=h[u];i;i=e[i].nxt){int v=e[i].to; //v为子节点
        if(v!=fa[u]){
            fa[v]=u,Dfs(v),dp[0][u]+=Min(dp[0][v],Min(dp[1][v],dp[2][v])), //转移dp[0][u]
            dp[2][u]+=dp[1][v],dp[1][u]+=Min(dp[0][v],dp[1][v]);  //转移dp[2][u]和dp[1][u]
            if(dp[0][v]<dp[1][v]) f=1;  //u点能做到安全,标记为1
            if(!f) fake=Min(fake,dp[0][v]-dp[1][v]); //否则尽量使损失变少
        }
    }
    if(!f) dp[1][u]+=fake; //u点还是不能做到安全,要手动在v上放人,用最少的损失
}
int main(){
    n=rd();
    rep(i,1,n){
        u=rd(),w[u]=rd(),m=rd();
        while(m--) v=rd(),Add(u,v),Add(v,u); //加边,注意输入数据不保证1没有父亲
    }
    Dfs(1),rt(Min(dp[0][1],dp[1][1])); //以1为根,最后根节点必须安全
    return flush(),0;
} //以下为读优输优主体~~喵喵喵~~

il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Len,stdin),p1==p2)?-1:*p1++;}
il int rd(){char c; int f=1;
    while(!isdigit(c=gc())&&c!='-');
    c=='-'?f=-1,c=gc():0; int x=c^48;
    while(isdigit(c=gc())) x=((x+(x<<2))<<1)+(c^48);
    return x*f;
}
il vd pc(char c){q1==duf+Len&&fwrite(q1=duf,1,Len,stdout),*q1++=c;}
il vd rt(int x){x<0?pc('-'),x=-x:0,pc((x>=10?rt(x/10),x%10:x)+48);}
il vd flush(){fwrite(duf,1,q1-duf,stdout);}

第一篇blog完结撒花!ヾ(@^▽^@)ノ

posted @ 2018-09-27 11:05 Shallowy 阅读(...) 评论(...) 编辑 收藏