P2458 [SDOI2006]保安站岗

P2458 [SDOI2006]保安站岗&& #10157. 「一本通 5.2 例 5」皇宫看守

眼熟吗?UVA1292 Strategic game#10156. 「一本通 5.2 例 4」战略游戏,最大独立集问题,感觉是不是一模一样?只不过是把边权转到点上了,做法一样,你设 \(f[u][0]\) 为不选,\(f[u][1]\) 为选,则……不细说,于是你获得了\(20\) 的好成绩。

其实仔细一想还真不一样,UVA和Loj10156 两题要求覆盖路径,而这两题是覆盖点, 那就决定了前者只用考虑两种决策,因为一条边只可能被两端的点覆盖;后者则可能被自己、儿子、父亲覆盖。

于是设:\(f[u][0]\) 为被自己覆盖,\(f[u][1]\) 为被父亲覆盖,\(f[u][2]\) 为被儿子覆盖,则:

\[f[u][0]= \displaystyle \sum_{v \in son[u]} min(min(f[v][0],f[v][1]),f[v][2]) \]

\[f[u][1]= \displaystyle \sum_{v \in son[u]} min(f[v][0],f[v][2]) \]

\[f[u][2]=min(f[v][0]-f[v][2])(v\in son[u])+ \displaystyle \sum_{v \in son[u]} min(f[v][0],f[v][2]) \]

对于一式,显然的,自己选不由子节点的状态决策,只需取 \(min\) 即可。

对于二式,既然被父亲覆盖了,则节点的状态只能由子节点选或者子节点被子节点的子节点选的 \(min\) 来更新。

对于三式,难理解,意思就是:如果节点被子节点覆盖了,那子节点要么自己覆盖,要么被子节点的子节点覆盖,但是有一个特殊情况,就是必须在子节点中选一个最小的 \(f[v][0]\) 选上,因为如果子节点都是由 \(f[v][2]\) 转移而来的,那么 \(u\) 的子节点就意味着一个也不选,自己就没人覆盖,所以判断这种情况并且要挑一个最小的换上。

/*
Knowledge : Rubbish Algorithm
Work by :Gym_nastics
Time : O(AC)
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int N=1e6+6;

int read() {
    int x=0,f=0;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
    return f?-x:x;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+48);
}

int fa[N],val[N],f[N][3],n,m;
int head[N],cnt;struct node{int v,nxt;}e[N];
void Add_edge(int u,int v){e[++cnt]=(node){v,head[u]};head[u]=cnt;}

void dfs(int u){
    f[u][0]=val[u];int Min=INF;bool flg=false;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;dfs(v);
        f[u][0]+=min(f[v][2],min(f[v][0],f[v][1]));
        f[u][1]+=min(f[v][0],f[v][2]);
        if(f[v][0]<f[v][2]){
            f[u][2]+=f[v][0];flg=true;
        } else{
            Min=min(Min,f[v][0]-f[v][2]);
            f[u][2]+=f[v][2];
        }
    }
    if(!flg) f[u][2]+=Min;
}
signed main() {
   n=read();int root=1;
   for(int i=1;i<=n;i++){
       int u=read(),k=read(),m=read();val[u]=k;
       for(int j=1;j<=m;j++){
           int v=read();Add_edge(u,v);
           fa[v]=u;
       }
   }while(fa[root])root=fa[root];dfs(root);
   print(min(f[root][0],f[root][2]));
   return 0;
}

posted @ 2022-03-23 21:58  Gym_nastics  阅读(28)  评论(0)    收藏  举报