最小驻扎问题

这个问题是我乱命名的

最小驻扎指的是:

图上每个点,可以付出代价并对其周围一定距离的点产生影响。

求最小的驻扎量,使得所有的点受影响。

又强行编了个定义

解决这类问题的方法,我知道两种

  • 动态规划
  • 贪心

DP太难了我不会,我只会贪心。

直接讲难的吧(反正简单的改一下就可以了

点我飞去luogu

狗屎测试题。

先讲思路(我抄袭的,from zt

对于最深的点,要想让它被控制,最优(不仅控制它,还可以控制尽量多的其他点)的方案一定是它的第k父亲。

以深度排序,从最深的开始检查:如果该点没有被控制,就在它的第k父亲放军队,并更新第k父亲可以影响到的点的状态。

虽然很暴力,但这是对的。

大家颅内证明一下

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,k,t;
vector <int> map[100001];//存图
struct yi//存点
{
    int posi;//点的编号
    int deep;//点的深度
    friend bool operator < (const yi &a,const yi &b)
    {
        return a.deep>b.deep;//重载 < 排序用
    }
}point[100001];//也可以写node[100001];
bool vis[100001];//是否访问过
int shen[100001];//存编号i的深度。和point[].deep一样,但这个从1~n方便用。
void dfs(int x,int d)//dfs求深度。x表示当前点,d表示当前点深度
{
    vis[x]=1;//标记,访问过了
    point[x].posi=x,point[x].deep=d;//存下编号及深度
    for(unsigned int i=0;i<map[x].size();i++)
        if(!vis[map[x][i]])//避免重复求
            dfs(map[x][i],d+1);
}
bool control[100001];//是否被控制
bool cmp1(const int &a,const int &b)
{
    return shen[a]<shen[b];//find()排序用的比较
}
inline int find(int x,int d)//查找最深节点的第k父亲。d最开始调用为k,不断-1,=0时走到第k父亲
{
    if(!d)
        return x;
    vis[x]=1;
    sort(map[x].begin(),map[x].end(),cmp1);//排序,原因请下拉
    for(unsigned int i=0;i<map[x].size();i++)
        if(!vis[map[x][i]])
            return find(map[x][i],d-1);//dfs找下去
    return x;//找不到,即没有第k父亲,只有第k-1父亲或k-2或...,返回本节点代替第k父亲
}
inline void change(int x,int d)//改变可影响到的节点的状态control[]
{
    control[x]=1;//已控制
    if(!d)//d递减进行dfs
        return ;
    vis[x]=1;//标记访问过
    for(unsigned int i=0;i<map[x].size();i++)
        if(!vis[map[x][i]])
            change(map[x][i],d-1);
}
int main()
{
    in(n);in(k);in(t);
    if(k==0)//特别判断垃圾情况,k==0每个点只能控制自己
    {
        cout<<n;
        return 0;
    }
    int ans=0;
    for(int i=1;i<n;i++)
    {
        int x,y;
        in(x);in(y);
        map[x].push_back(y);
        map[y].push_back(x);//vector最高
    }
    dfs((1+n)>>1,1);//求深度的起点可以随意,我选择(1+n)>>1
    sort(point+1,point+n+1);//从1开始存的
    for(int i=1;i<=n;i++)
        shen[point[i].posi]=point[i].deep;//预先用shen[]存好节点深度,方便找 排序
    for(int i=1;i<=n;i++)
        if(!control[point[i].posi])//如果当前最深点没被控制
        {
            memset(vis,0,sizeof(vis));//初始化
            int dad=find(point[i].posi,k);//找第k父亲。懒得想变量名,直接dad
            memset(vis,0,sizeof(vis));//初始化
            change(dad,k);//更新状态
            ans++;//更新答案
        }
    cout<<ans;
    return 0;
}

sort的原因

bool cmp1(const int &a,const int &b)
{
    return shen[a]<shen[b];
}
inline int find(int x,int d)
{
    if(!d)
        return x;
    vis[x]=1;
    sort(map[x].begin(),map[x].end(),cmp1);
    for(unsigned int i=0;i<map[x].size();i++)
        if(!vis[map[x][i]])
            return find(map[x][i],d-1);
    return x;
}

我们从最深的节点开始找第k父亲。但在树上(这题),一个点的第k父亲,可能不止一个,我们只选择最优的贪心。最优的第k父亲,一定是深度最小的第k父亲。因为贪心的目的就是由深到浅进行控制,越早控制浅的就越优,所以进行排序,对深度最浅的进行dfs找第k父亲。

然后就A啦。

视奸dalao的不同写法的代码

huangwenlong,huangwenlong

Toby_ZT

mnyhome

还有好多,全部放下来的话下拉体验很差

修改一下输入方法,这题也轻松A

posted @ 2017-11-03 11:16  syhien  阅读(117)  评论(0编辑  收藏  举报