[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完结撒花!ヾ(@^▽^@)ノ

转载于:https://www.cnblogs.com/Shallowy/p/9712013.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值