#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; }
【HDU4313】 - Matrix - 树状DP Version 思路+解题报告+AC代码【0.4%达成】
最新推荐文章于 2019-11-15 16:45:24 发布