#专题练习# 强连通分量,缩点
1.洛谷 P1455 搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有n朵云,云朵已经被老板编号为1,2,3,……,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。
输入格式
第1行n,m,w,表示n朵云,m个搭配和你现有的钱的数目
第2行至n+1行,每行ci,di表示i朵云的价钱和价值
第n+2至n+1+m ,每行ui,vi表示买ui就必须买vi,同理,如果买vi就必须买ui
输出格式
一行,表示可以获得的最大价值。
Q:题意是:有n个云朵,每个云朵有自己的价格ci 和价值di ,小明想用手里的钱买到尽可能的大的价值。但是老板的云朵们之间具有固定搭配方案,不能拆开购买。小明想知道他能购买到的最大价值是多少?
A:依题意可知,云朵之间存在搭配,即每种搭配是一个强连通分量。第一步,我们找出每个强连通分量,并计算出每个强连通分量的总价格和总价值;第二步,我们计算小明现有的钱可以买到的最大价值,显然这是一个简单的01背包,但是这里用二维背包会超时,需要用滚动数组优化为一维。这样就结束了。(好像并查集+01背包也能写,下次想起来补上。)
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<stack> 6 using namespace std; 7 const int maxv= 10010; 8 const int maxe= 10100; //可能的最大值 9 10 struct ENode 11 { 12 int to; 13 int Next; 14 }; 15 ENode edegs[maxe]; 16 int Head[maxv], tnt; 17 void init() 18 { 19 memset(Head, -1, sizeof(Head)); 20 tnt= -1; 21 } 22 void Add_ENode (int a, int b) 23 { 24 ++ tnt; 25 edegs[tnt].to= b; 26 edegs[tnt].Next= Head[a]; 27 Head[a]= tnt; 28 ++ tnt; 29 edegs[tnt].to= a; 30 edegs[tnt].Next= Head[b]; 31 Head[b]= tnt; 32 } 33 34 int m; 35 int dfn[maxv]; //深度优先搜索中顶点被访问的时间 36 int low[maxv]; //顶点v 和它的邻接点中low[]的最小值 37 int temp[maxv]; //判断节点是否已被访问过(0-未访问 1-已访问未删除 2-已访问已删除) 38 int Stack[maxv]; //手动栈 39 int TarBfs (int v, int lay, int &scc_num) 40 { 41 /*v: 新加入的点; lay: 时间戳; scc_num: 记录强连通分量的数量*/ 42 43 /*第2步:初始化dfn[v]和low[v]*/ 44 temp[v]= 1; 45 low[v]= lay; 46 dfn[v]= lay; 47 Stack[++ m]= v; 48 for (int k= Head[v]; k!= -1; k= edegs[k].Next) 49 { 50 /*对于v 的所有邻接节点u:*/ 51 int u= edegs[k].to; 52 if (temp[u]== 0) 53 { 54 /*第2-1步:如果没有访问过,则跳转执行第2步,同时维护low[v]*/ 55 TarBfs(u, ++ lay, scc_num); 56 57 } 58 if (temp[u]== 1) 59 { 60 /*第2-2步:如果访问过,但没有删除,维护low[v]*/ 61 low[v]= min(low[v], low[u]); 62 } 63 64 } 65 if (dfn[v]== low[v]) 66 { 67 /*如果low[v]== dfn[v],则当前节点是一个强连通分量的根, 68 那么输出相应的强连通分量。*/ 69 ++ scc_num; 70 do 71 { 72 low[Stack[m]]= scc_num; 73 temp[Stack[m]]= 2; //已删除的节点temp更新为2 74 }while (Stack[m --]!= v); 75 } 76 return 0; 77 } 78 79 int Tarjan(int n) 80 { 81 int scc_num= 0, lay= 1; 82 m= 0; 83 memset(temp, 0, sizeof(temp)); 84 memset(low, 0, sizeof(low)); 85 for (int i= 1; i<= n; i ++) 86 { 87 if (temp[i]== 0) 88 { 89 /*第1步:找一个没有被访问过的节点v,否则算法结束*/ 90 TarBfs(i, lay, scc_num); 91 } 92 } 93 /*返回强连通分量的个数*/ 94 return scc_num; 95 } 96 97 int ci[maxv]; 98 int di[maxv]; 99 int rock_ci[maxv]; //每个连通块的费用和 100 int rock_di[maxv]; //每个连通块的价值和 101 int dp[10010]; 102 int main() 103 { 104 int n, m, cost; 105 int a, b; 106 scanf("%d %d %d", &n, &m, &cost); 107 for (int i= 1; i<= n; i ++) 108 { 109 scanf("%d %d", &a, &b); 110 ci[i]= a; 111 di[i]= b; 112 } 113 init(); 114 for (int i= 0; i< m; i ++) 115 { 116 scanf("%d %d", &a, &b); 117 Add_ENode(a, b); 118 } 119 120 int ans= Tarjan(n); 121 memset(rock_ci, 0, sizeof(rock_ci)); 122 memset(rock_di, 0, sizeof(rock_di)); 123 for (int i= 1; i<= n; i ++) 124 { 125 int tmp= low[i]; 126 rock_ci[tmp]+= ci[i]; 127 rock_di[tmp]+= di[i]; 128 } 129 int sum= 0; 130 // for (int i= 1; i<= ans; i ++) 131 // { 132 // printf("--> %d %d\n", rock_ci[i], rock_di[i]); 133 // } 134 // cout << cost << endl; 135 for (int i= 1; i<= ans; i ++) 136 { 137 for (int j= cost; j>= 0; j --) 138 { 139 if (j- rock_ci[i]>= 0) dp[j]= max(dp[j], dp[j- rock_ci[i]]+ rock_di[i]); 140 else dp[j]= dp[j]; 141 // printf("-%d- -%d- -%d-\n", dp[i][j], dp[i- 1][j], rock_di[i]); 142 } 143 } 144 printf("%d\n", dp[cost]); 145 return 0; 146 }
end;