(贪心)NOIP模拟题:引爆炸弹

是否有环往往很大程度上影响着一道关于图的问题。

描述 Description(Time Limit: 1s ; Memory Limit 128MB)

有n个炸弹,有些炸弹牵了一根单向引线(也就是说引线只有在这一端能被炸弹点燃),只要引爆了这个炸弹,用引线连接的下一个炸弹也会爆炸。每个炸弹还有个得分,当这个炸弹被引爆后就能得到相应得分。

现在要你引爆k个炸弹,使得分最大。

输入格式 InputFormat

第1行两个整数n、k。

接下来n行每行两个整数a[i]、b[i]。a[i]表示这个炸弹用引线连接的下一个炸弹,如果a[i]为0,则表示这个炸弹没连接引线。b[i]表示这个炸弹的得分。

输出格式 OutputFormat

仅一个数,表示最大得分。

样例输入 SampleInput

8 2

0 1

1 1

2 100

2 100

0 1

5 1

6 2

6 2

样例输出 SampleOutput

202

数据范围和注释 Hint

1≤b[i]≤1000000

对于30%的数据,n≤1000 k≤30

对于60%的数据,n≤50000 k≤100

对于100%的数据,n≤200000 k≤500

这个题有没有环呢?

我们的数据里没有。

因此就可以预处理出所有 叶子节点 的最大连锁爆炸值,然后降序排序;

接着按顺序对每个连锁爆炸进行处理:如果它的连锁爆炸过程中有之前已经爆炸过的炸弹,就减掉这颗炸弹的爆炸值;否则把这颗炸弹标记为已爆炸。

然后再排一遍序,从大到小记录k个值的和,输出即可。(要开long long 啊)

<span style="font-size:18px;"><strong>#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n,k;

struct zhadan
{
	int w,next;
}b[200010];
struct defen
{
	int i;
	long long fen;
	friend bool operator < (defen a,defen b)
	{
		return a.fen>b.fen;
	}
}s[200010];

long long dfs(int x)
{
	if(x==0) return 0;
	if(s[x].fen>0) return s[x].fen;
	int ne=b[x].next;
	return s[x].fen=b[x].w+dfs(ne);
}

bool vis[200010];

int main()
{
	freopen("bomb.in","r",stdin);
	freopen("bomb.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&b[i].next,&b[i].w);
		s[i].i=i;
	}
	for(int i=1;i<=n;i++) dfs(i);
	sort(s+1,s+n+1);
	int u,ne;
	for(int i=1;i<=n;i++)
	{
		u=s[i].i;
		ne=b[u].next;
		if(vis[u]) s[i].fen-=b[u].w;
		vis[u]=1;
		while(ne!=0)
		{
			if(vis[ne]) s[i].fen-=b[ne].w;
			vis[ne]=1;
			ne=b[ne].next;
		}
	}
	sort(s+1,s+n+1);
	long long ans=0;
	for(int i=1;i<=k;i++) ans+=s[i].fen;
	printf("%I64d\n",ans);
	return 0;
}</strong></span>

当然,如果有环的话就要涉及到缩点了(喜(sang)闻(xin)乐(bing)见(kuang))。

<span style="font-size:18px;color:#009900;"><strong style="background-color: rgb(255, 102, 102);">#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
//注意一个炸弹可能被多个导线引爆,但是最多引爆下一个炸弹,这个可能很关键
//那么如果倒过来建图,就是一个森林
//建图的时候似乎可以缩边优化
//如果有环,直接缩点,并且缩点完了一定是根
//贪心一定是对的,每次选一条树上的最长路,Bomb!

//Global Variables
int n,k;
int w[200010];
//End Global Variables  template<typename _Tp>

//Map
namespace MAP{
int h[200010];

struct edge
{
    int v,next;
}e[200010];

int ecnt = -1;
inline void adde(int u,int v)
{
    ecnt++;
    e[ecnt].v = v;
    e[ecnt].next = h[u];
    h[u] = ecnt;
}

inline void reset()
{
    memset(h,-1,sizeof(h));
    ecnt = -1;
}
}
//End Map

//Tree
namespace TREE{
using namespace MAP;
//Variables
    //左儿子右兄弟
    struct node
    {
        int num;
        int l,r,f;
        long long maxpathsum;
        int maxpathson;
        long long w;

        node(int num = -1,int f = -1,long long w = 0,int l = -1,int r = -1):num(num),f(f),w(w),l(l),r(r){}
        int Addson(long long w);
        void Setson(int n);
        long long maxpath();
        void Del();

        void _DFS();
    }T[200010];

    int Tcnt = 0;
    int node::Addson(long long w)
    {
        T[Tcnt] = node(Tcnt,this -> num,w,-1,-1);
        this -> Setson(Tcnt);
        return Tcnt++;
    }

    void node::Setson(int n)
    {
        T[n].f = this -> num;
        if(this -> l == -1)
        {
            this -> l = n;
        }else{
            int i = this -> l;
            for(;T[i].r != -1;i = T[i].r);

            T[i].r = n;
        }
    }

    long long node::maxpath()
    {
        long long rv = 0;

        this -> maxpathson = -1;
        if(this -> l == -1)
        {
           // PRINT("maxpath[%d] = %lld\n",this -> num,this -> w);
            return this -> maxpathsum = this -> w;
        }else{
            for(int i = this -> l;i != -1;i = T[i].r)
            {
                T[i].maxpath();
                if(T[i].maxpathsum > rv)
                {
                    rv = T[i].maxpathsum;
                    this -> maxpathson = i;
                }
            }
            //PRINT("maxpath[%d] = %lld\n",this -> num,rv + this -> w);
            return this -> maxpathsum = (rv + this -> w);
        }
    }

struct gr
{
    bool
    operator()(int & _x,int & _y) const
    { return T[_x].maxpathsum < T[_y].maxpathsum; }
};
priority_queue<int,vector<int>,gr>roots;

    void node::Del()
    {
        for(int i =this -> l;i != -1;i = T[i].r)
        {
            if(i == this->maxpathson)
            {
                T[i].Del();
            }else{
                roots.push(i);
            }
        }
    }

    void node::_DFS()
    {
        //PRINT("%d\n",this -> num);

        for(int i = this -> l;i != -1;i = T[i].r)
        {
            T[i]._DFS();
        }

        //PRINT("~%d\n",this -> num);
    }
//End Variables
//Functions
//建树
int vis[200010];
int cn[200010];
int isroot[200010];

void DFS(int u,int vn,int & is_circle)
{
    //PRINT("%d %d\n",u,cn[u]);
    vis[u] = vn;

    int sub_circle = 0;
    int thi_circle = 0;
    for(int i = h[u];i != -1;i = e[i].next)
    {
        int v = e[i].v;
        if(vis[v])
        {
            if(vis[v] == vn)
            {
                //成环了
                thi_circle = 1;
            }else{
                //连上一个访问过的子树,则子树的根一定不是缩出来的点
                T[cn[u]].Setson(cn[v]);
                isroot[cn[v]] = 0;
            }
        }else{
            cn[v] = T[cn[u]].Addson(w[v]);
            isroot[cn[v]] = 0;
            DFS(v,vn,sub_circle);

            if(sub_circle)
            {
                if(T[cn[u]].l == cn[v])
                {
                    T[cn[u]].l = T[cn[v]].r;
                }else{
                    for(int i = T[cn[u]].l;i != -1;i = T[i].r)
                    {
                        if(T[i].r == cn[v])
                        {
                            T[i].r = T[cn[v]].r;
                            break;
                        }
                    }
                }
            }

        }
    }

    if((sub_circle || thi_circle) && isroot[cn[u]] == 0)
    {
        //PRINT("merge %d\n",u);
        is_circle = 1;
        //Merge to father
        int f = T[cn[u]].f;
        if(T[cn[u]].l != -1)
        {
            T[f].Setson(T[cn[u]].l);
            for(int i = T[cn[u]].l;i != -1;i = T[i].r)
            {
                T[i].f = f;
            }
        }

        T[f].w += T[cn[u]].w;
    }
}

void Build()
{
    memset(vis,0,sizeof(vis));

    for(int ii = 0;ii < 200010;ii++){
        isroot[ii] = 1;
    }

    for(int i = 1;i <= n;i++)if(!vis[i])
    {
        int temp;
        cn[i] = Tcnt++;
        T[cn[i]] = node(cn[i],-1,w[i]);
        DFS(i,i,temp);
    }

    for(int i = 0;i < Tcnt;i++)if(isroot[i])
    {
        T[i].maxpath();
        //T[i]._DFS();
        roots.push(i);
    }
}
//初始化计算
void Init()
{
    return;
}
//一次贪心
long long Do()
{
    int u = roots.top();roots.pop();
    T[u].Del();
//PRINT("choose %d %lld\n",u,T[u].maxpathsum);
    return T[u].maxpathsum;

    Init();
}
}
//End Tree

//Main Structure
inline void ir()
{
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);

    scanf("%d%d",&n,&k);

    MAP::reset();
    int a;
    for(int i = 1;i <= n;i++)
    {
        scanf("%d%d",&a,&w[i]);
        //反向加边
        MAP::adde(a,i);
    }
}

int main()
{
    ir();

    TREE::Build();
    TREE::Init();

    long long ans = 0;
    for(int i = 0;i <k;i++)
    {
        ans += TREE::Do();
    }

    cout << ans << '\n';
    return 0;
}</strong></span><span style="font-size:14px;">
</span>
时间: 2024-08-04 03:43:02

(贪心)NOIP模拟题:引爆炸弹的相关文章

[BZOJ入门OJ2092][Noip模拟题]舞会

2092: [Noip模拟题]舞会 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 9  Solved: 5 [Submit][Status][Web Board] Description 学校举行舞会啦,一共有N个人参加,所有人站成一排,从左开始编号,最左边的人编号为1 ,最右边的为N.每个人跳舞的熟练度我们用一个整数表示,第i个人的熟练度为Ai,每次熟 练度最接近的一对相邻男女会出列跳舞,如果有多对那么最左边的那一对会先出列,请你给 出出列跳

8.22 NOIP 模拟题

  8.22 NOIP 模拟题 编译命令 g++ -o * *.cpp gcc -o * *.c fpc *.pas 编译器版本 g++/gcc 4.9.2 fpc 2.6.2 评测环境 64 位 Linux, 3.3GHZ CPU 评测软件 Lemon 评测方式 忽略行末空格和回车 特别注意:c/c++ 选手使用 printf 输出 64 位整数请使用%lld 1 注意事项 A 债务 文件名                            输入文件             输出文件  

蓝桥杯模拟赛-引爆炸弹-DFS+并查集

今天整理电脑,翻出来了很久以前大佬给的题,贴一下. 引爆炸弹 1000ms 在一个 n×m的方格地图上,某些方格上放置着炸弹.手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去. 现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆. 输入格式 第一行输两个整数 n,mn,mn,m,用空格隔开. 接下来 nnn 行,每行输入一个长度为 mmm 的字符串,表示地

noip模拟题题解集

最近做模拟题看到一些好的题及题解. 升格思想: 核电站问题 一个核电站有N个放核物质的坑,坑排列在一条直线上.如果连续M个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质. 任务:对于给定的N和M,求不发生爆炸的放置核物质的方案总数 输入:输入文件只一行,两个正整数N,M( 1<N<50,2≤M≤5) 输出:输出文件只有一个正整数S,表示方案总数. 运用升格思想.设N个坑不会发生爆炸的方案数是f[N],那么我们假设N以前的坑的方案 都已知了,那么我们只需要考虑第N个坑如何放即可(顺

【noip模拟题】天神下凡(贪心)

vijos某次模拟赛原题... 处理出每个圆的一级祖先就行了... 其实没有那么麻烦,贪心即可出解. 我们将每个圆转换成线段后按左端点小右端点大的方法排序 然后维护一个栈: 对于每一个圆i 如果栈顶右端点比圆i的右端点小,则出栈,直到栈空 否则i的一级祖先就是栈顶,并且加入i到栈. 证明: 因为左端点排序,所以问题转换为找一个最小的右端点能够包含此线段 假如栈顶的右端点比当前右端点小,显然对于所有将来的线段,不可能包含将来的线段(或者说,尽管能,也不是最优解,因为最优解就是当前i) 然后如果有n

NOIP模拟题 2017.7.3 - 模拟 - 贪心 - 记忆化搜索

直接暴力模拟,注意判数据结构为空时的取出操作. Code 1 #include<iostream> 2 #include<cstdio> 3 #include<ctime> 4 #include<cctype> 5 #include<cstring> 6 #include<cstdlib> 7 #include<fstream> 8 #include<sstream> 9 #include<algorit

9.22 NOIP模拟题

吉林省信息学奥赛 2017 冬令营                                                                                                                                                                                                      day2 嗯 今天题目好水 嗯 今天爆零了好伤心...... 值得纪念

6.19 noip模拟题(题目及解析转自 hzwer 2014-3-15 NOIP模拟赛)

Problem 1 高级打字机(type.cpp/c/pas) [题目描述] 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这种高级打字机设计一个程序,支持如下3种操作: 1.T x:在文章末尾打下一个小写字母x.(type操作) 2.U x:撤销最后的x次修改操作.(Undo操作) (注意Query操作并不算修改操作) 3.Q x:询问当前文章中第x个字母并输出.(Query操作) 文章一开始可以视为空串. [输入格式] 第1行:一个整数n,表

XJOI NOIP模拟题1

第一题 分析: 开始想的是贪心,取每列均值最大一段. 应该是01分数规划,具体看代码 代码: program gold; var a:array[0..100000]of int64; n,i,m,j,x:longint; function max(x,y:real):real; begin if x>y then max:=x else max:=y; end; function cheak(x:real):boolean; var i,j:longint; s,ans:real; begin