#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
/**
Problem: HDU4313 - Matrix - DP Version【0.4%达成】
Copyright : 归我们学校集训队和本人所有,未经同意可以转载。
【但是胆敢用于培训的话,贵学校服务器将经受本人长时间免费义务压力测试,CC+TCP碎片+模拟访问,至少1000 Zombies以上】
【请做好心理准备】
Thanks:感谢提供帮助的 Dragon ,zjj , gzm , lqy , zsw,lpp等大神……
= =多谢你们的patient,排名不分先后。。。
Reference:——【凡是给出Reference的,底下的内容就是参考Reference和本人思路写的,如有错误欢迎指正,如不希望本人引用请mail 0daydigger#gmail.com】
http://blog.csdn.net/cyberzhg/article/details/7790486 ——写的很清晰的代码,强烈推荐
http://page.renren.com/601081183/note/862977450 ——这个。。其实没给dp怎么写。。。凑合看吧
Knowledge Point:邻接表存储,树状dp
傻逼错误:把<看成了>导致理解不能
傻逼错误II:妈的居然忘记了邻接表存储这个事儿!!
Thought:
首先,是关于那个奇怪的addEdge,好吧,图论那章我还没刷呢。
首先证明addEdge函数能正确的存储图的邻接表。
邻接表只存储与节点i相邻的节点信息——这句话说给基础不好的童鞋【就是你自己吧喂!
初始:
初始化的时候,head[]={-1},edge[]中没有存储东西,在第一对节点(u,v)被存储的时候,
edge[v].next = head[u] == -1;
edge[u].next = head[v] == -1;
head[u] = v在edge[]中的下标
head[v] = u在edge[]中的下标
那么从head[u]和head[v]开始遍历的话,就能遍历所有与u和v相邻的节点。
保持:
对于(u',v'),如果(u',v')都是新节点的话,那么在【初始】中已经证明了其正确性。
如果有一个不是新节点的话,设u'不是新节点。
那么【u',v'指的是edge[]中存储u',v'两个点的下标,有时候也指节点本身,请按照上下文区分】
head[u']先被更新,指向存储了v'信息的edge[]的下标。
然后head[v']更新,存储了指向u'信息的edge[]的下标。
由于有一句话
edge[edgeNum].next = head[u']
...
head[u'] = edgeNum++
那么edge[v']中保留的就是以前head[v']中的信息,使得
for(i = head[u]; i != -1 ; i = head[u].next ) 就可以用edge[i]来遍历u的邻接表了。
如果两个节点都是老节点的话,同理
终止
根据【保持】中的分析,得到(u,v)的时候,可以
addEdge(u,v,w);
addEdge(v,u,w);便可以使得图中的每个点都建立起邻接表了。
——————————————————————接下来是dp部分的证明————————————————————
dp[][],第一个维度存储节点,第二个维度只有2个大小
dp[u][0]表示当u为根的子树(包含u哦,下同)中不含机器时【需要耗费多少时间】
dp[u][1]表示当u为根的子树中含有一台机器的时候【需要耗费多少时间】
那么,当u为叶子节点,且u有机器(即color[u] = BLACK)的时候:
dp[u][0] = INFINITE;
dp[u][1] = 0;
u是叶子节点,u不含有机器(color[u] = WHITE)的时候
dp[u][0] = 0;
dp[u][1] = INFINITE;
那么,当u为非叶子节点的时候,对于【全部u的邻接表中的节点v】(←这句话很重要!阅读下文的时候请务必牢记!)采取以下策略:
————————————————————嫌啰嗦就直接看最后一部分!——————————————
①如果u有机器
那没的说了,dp[u][0]这时候就废了。只能用dp[u][1]了
dp[u][1] += min(dp[v][0],dp[v][1] + w); //v是u的邻接表中所有节点
这个方程的意思是说,因为u本身已经有机器了,那么dp[u][1]只要选
dp[v][0]或者dp[v][1]+w就好,dp[v][1]+w是指把(u,v)这条边减下去,选这俩小的就好。
②如果u没有机器
dp[u][0] += min(dp[v][0],dp[v][1]+w);
还是一样,如果u没有机器的话,那么把dp[v][0]和dp[v][1]+w选一个小的就好,如果v有一个机器的话
只要删了u与v连着的这条边就可以了。
但是我们还要考虑dp[u][1]呢。
如果我们选择了dp[v][0]:
此时只要在消耗的时间中【减去】【最大的】dp[v][1] - dp[v][0]就好
亦即 dp[u][1] = dp[u][0] + minEdge (minEdge = dp[v][1] - dp[v][0],dp[v][1]一定比dp[v][0]要小,因为dp[v][0]要多剪掉一刀)
因为dp[v]中存储的可都是节点v的最优状态,我们从邻接节点v中选出来一条权值最大的带机器的边的权值从dp[u][0]中减去
直接得到的就是dp[u][1]的最优解。
如果我们选择了dp[v][1]+w
此时就是minEdge与 -w 比较,如果minEdge 比较大的话,minEdge = -w;
遍历中开一个变量cnt记录u的子树个数,如果u>0的话,且u没有机器那么遍历结束后:
dp[u][1] = dp[u][0] + minEdge( minEdge是负的 )
————————————给嫌啰嗦的人看——————————
总之!anyway!我们就是要在节点u的所有后代v中找出来一条权值最大并且连接带机器的点的边(卧槽真尼玛绕嘴)
从dp[u][0]中减去其权值,那么直接就得到了dp[u][1]的最优解。
具体实现是用DFS实现的(总有种感觉裸搜也能过……)
Q.E.D.
*/
const int WHITE = 0;
const int BLACK = 1;
const int MAX_SIZE = 100005;
const __int64 INFINITE = 100000000000LL;
struct node
{
__int64 v;
__int64 w;
int next;
};
__int64 dp[MAX_SIZE][2];
node edge[MAX_SIZE*2];
int head[MAX_SIZE];
int edgeNum;
char color[MAX_SIZE];
void addEdge(int u,int v,int w)
{
edge[edgeNum].v = v;
edge[edgeNum].w = w;
edge[edgeNum].next = head[u];
head[u] = edgeNum++;
}
void dfs(int u,int father)
{
long long minEdge = INFINITE;
int cnt = 0;
int v = 0;
int w = 0;
if( color[u] == BLACK ) //has machine
{
dp[u][0] = INFINITE;
dp[u][1] = 0;
}
else
{
dp[u][0] = 0;
dp[u][1] = INFINITE;
}
for(int i = head[u]; i != -1 ; i = edge[i].next)
{
v = edge[i].v;
w = edge[i].w;
if( v != father )
{
dfs(v,u);
if( color[u] )
{
dp[u][1] += min(dp[v][0],dp[v][1] + w);
}
else
{
dp[u][0] += min(dp[v][0],dp[v][1] + w );
cnt++;
if ( dp[v][0] <= dp[v][1] + w)
{
if( minEdge > dp[v][1] - dp[v][0] )
minEdge = dp[v][1] - dp[v][0];
}
else
{
if( minEdge > -w )
minEdge = -w;
}
}
}
}
if( color[u] == WHITE && cnt > 0 )
dp[u][1] = dp[u][0] + minEdge;
}
int main()
{
int T;
int N,K;
int u,v,w;
#ifndef ONLINE_JUDGE
freopen("B:\\acm\\SummerVacation\\DP-II\\C.in","r",stdin);
freopen("B:\\acm\\SummerVacation\\DP-II\\C.out","w",stdout);
#endif
while(scanf("%d",&T) != EOF)
{
for(int t = 1 ; t <= T ; t++)
{
memset(head,-1,sizeof(head));
memset(color,0,sizeof(color));
edgeNum = 0;
memset(edge,0,sizeof(edge));
scanf("%d%d",&N,&K);
for(int i = 1 ; i < N ; i++)
{
scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
addEdge(v,u,w); //构造连接表
}
for(int i = 0 ; i < K ; i++)
{
scanf("%d",&u);
color[u] = BLACK;
}
dfs(0,-1);
if( color[0] == WHITE )
{
printf("%I64d\n",min(dp[0][0],dp[0][1]));
}
else
{
printf("%I64d\n",dp[0][1]);
}
}
}
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}
相关推荐
HDU 4313 最小生成树
题解 : 这个题目我们可以把边的权值从大到小排序 ,为什么这样排序呢,因为在排序的时候我们可以按照从大到小依次将边加入到最小生成树中去,加入进去后我们可以发现一个,当两个点连载一起时候加入的那条边就是要删去的边 因为这是这两个点之间最小的一条边。 还需要注意在合并的时候我们必须保证根节点是要被破坏的点这样就可以了 具体实现看代码了#include <iostream> #include <cstri
hdu 4313 Matrix (kruskal模板题,cmp倒着取边)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4313 题意:有n个城市,有n-1条边连通,其中的k个城市有破坏机器,要求切断部分路,使得这k个城市之间不能连通,每条路对应不同的花费时间,求最少花费。 思路:以含“破坏机器”的城市作为根结点,分为k棵树,每加入一条边,判断该边的两个端点所对应的祖先是不是都含“机器”,若都含不连这条边直接在ans上加,...
hdu4313 Matrix(kruskal思想+并查集)
题意: 给定一棵n个节点的树,树边有边权 现在有k个坏点,要求删除树中若干条边,满足k个坏点两两不连通,问删除的最小边权和是多少 数据范围:n<=1e5 解法: 反过来想,将问题转化为计算能保留的最大边权和 根据kruskal算法思想贪心地对边按边权从大到小排序,然后遍历, 如果当前边不会令两个坏点相连那么可以保留,否则必须删 累加必须删的边的边权就能得到答案。 判断连接之后是否会使两个坏点相连可以用带权并查集维护连通块内坏点数量 code: #include<bits/stdc++.h>
HDU 4313 Matrix
人一我百人十我万!!! 一开始想用最短路来求…结果发现节点好多。。。 发现是最小生成树里面的克鲁斯卡思想,加一些贪心就好了。 ac code:#include #include #include #include #include #include #include #include #include #include #include #inc
HDU 4313
这个题有两种做法 1.并查集 初始时一条边都不加,将所有边按权值从大到小排序,然后依次判断每一个边两端的顶点是否是均为machine节点,如果是则应删除这条边,否则加入这条边,然后在并查集合并时尽量让根节点为machine节点。 #include<cstdio> #include<cstring> #include<cstdlib> #include<...
hdu4313(图,和最小生成树类似)
Description Machines have once again attacked the kingdom of Xions. The kingdom of Xions has N cities and N-1 bidirectional roads. The road network is such that there is a unique path b...
HDU4313Matrix(Kruskal算法求解)
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3538 Accepted Submission(s): 1405 Problem Description Machines have once again a...
HDU 4313 并查集
点击打开链接 题意:给一个城镇的图,有m个城镇上是有敌兵的,为了将所有敌兵的联系隔断,需要删除的所有边的最小的权值 思路:看了就知道是并查集Kruskal思想的题,我们将边的权值从大到小向里面加,如果我要加的这条变加进去之后,敌兵可以相连,那么这条边肯定要删下去,而我们从大到小加的边,所以肯定是最小的,因为可以的边我们都用上了嘛#include #include #include
HDU 4313 Matrix(贪心+并查集)
HDU 4313 题意: 有n个节点,n-1条边,其中k个节点为危险节点,有大规模杀伤性武器,切断哪些路能使得这些大规模杀伤性武器的危险节点之间彼此不连通,且切断的边权值之和最小。 思路: 初始化每个节点为一个集合,并记录每个集合中危险节点的数目(0或1)。 要实现权值之和尽可能的小,则要权值尽可能小,故先将n-1条边按权值先升序排序。 排序后枚举这些边: 若边的两端节点所在集合
!HDU 4313 破坏导弹攻城计划-连通图-(最小生成树变形)
题意:有n个城市,城市之间有n-1条路连接每个城市,也就是说有一个树,敌人在一些城市放了导弹,共有m个导弹,敌人想把导弹运到一起然后开战,现在给你个任务就是摧毁一些路,使任何两个导弹都不能运到一起,摧毁每条路都有相应的代价,求最小的代价。 分析: 导弹不能运到一起就是说任何两个导弹不能在一个连通图中,所以我们的任务就是用最少的代价摧毁一些路使m个导弹分别在m个连通图中。 这题是就反面,最少的
HDU 4313 Matrix (Kruskal应用)
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2498 Accepted Submission(s): 940 Problem Description Machines have once again
HDU4313 Matrix【Kruskal算法+并查集】
MatrixTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3532 Accepted Submission(s): 1401Problem DescriptionMachines have once again attacked t...
hdu 4313 - Matrix(最小生成树,并查集)
题目: Matrix 题意: N个点N-1条无向边,K个数,要求去掉一些边,使得这K个数相互不可达,求去掉的边的权值和的最小值 思路: 这K个数不能相互到达,说明这k个数分别属于不同的集合,我们把可以放进同一集合的数作为同一集合,不能放进的,则把这条边舍弃。集合自然想到用并查集。要找最小值,我们可以把所有边按权值从大到小排序,顺序寻找,找到不能放进的则跳过,res
hdu 4313 Matrix (最小生成树krusual)
题目链接 题目意思:有多个城市,多条路,路都是双向的,有一些城市有机器人大军,我们想通过破坏城市之间的城市来断绝机器人大军的联系,并且用最少的时间。 用krusual算法,并用mark标记存在有机器人的城市,有的两个城市之间的道路只有的1个机器人大军,剩下的就是要破坏的边。。。 #include"stdio.h" #include"stdlib.h" #include"strin...
HDU 4313 Matrix 贪心 || 树形dp
题意:给定n(n 题解:1贪心:类似Kruskal的贪心过程,以每个危险的节点为并查集的根节点,附代码不赘述。 2树形dp:dp[i][0]代表的是在当前以i节点为根节点的子树中,i所在的连通块中没有危险节点的最小花费; dp[i][1]代表的是在当前以i节点为根节点的子树中,i所在的连通块中有危险节点的最小花费;
hdu 4313 Matrix
Problem Description Machines have once again attacked the kingdom of Xions. The kingdom of Xions has N cities and N-1 bidirectional roads. The road network is such that there is a unique path betw
HDU4313Matrix(用最小生成树思想,集合划分)
Matrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2598 Accepted Submission(s): 973 Problem Description Machines have once again
HDU4313
HDU 4313 Matrix /* dp[i][0]代表的是在当前以i节点为根节点的子树中,i所在的连通块中没有危险节点的最小花费; dp[i][1]代表的是在当前以i节点为根节点的子树中,i所在的连通块中有一个危险节点的最小花费; 注意:是i所在的连通块允许出现一个危险节点 如果i是叶子节点:如果i为危险点dp[i][0] = inf,dp[i][1]= 0;否则dp[i][0] = dp...
HDU 4313 Matrix (贪心+并查集)
题意:给你一个有n(2 思路: 正着思考的确不好入手,反着思考,让机器人不能互达,所以独立成一个集合,可以用并查集,然后模拟加边,若加上的边让机器人互连那么就删去这条边,可见,让删去的边越短越好,所以尽可能先添加长边。 //2776 KB 218 ms #include #include #include #include using namespace std; struct node {
284




被折叠的 条评论
为什么被折叠?



