题目详情
n个节点,m条边的无向图,每个节点一个权值w。定义拆除一个节点的代价为与其相邻的节点的权值之和。拆除一个节点后删除所有与该节点相连的边。求拆除所有节点需要花费的最少代价。输入描述:输入包含多组测试数据,每组测试数据第一行先输入n,m(1 ≤ n ≤ 10000; 0 ≤ m ≤ 20000),第二行输入n个整数wi(0 ≤ wi ≤ 105),接下来的m行,每行两个整数u,v代表节点u与v相连(1 ≤ ui, vi ≤ n; ui ≠ vi).输出描述:对于每组测试数据,输出拆除所有节点需要花费的最少代价。
答题说明
样例输入:
4 3
10 20 30 40
1 4
1 2
2 3
样例输出:
40
Note:
其中一种拆除策略是:
先拆除节点3,代价为20
再拆除节点2,代价为10
再拆除节点4,代价为10
最后拆除节点1,代价为0
所以总的代价=20+10+10+0=40
解析:我以为这道题要用贪心算法:每次都删掉最小权值的点.所以我就写了这么多.答案是错误.
而实际上,没必要,每条边都要删掉的!只要把边的一端删掉就可以删掉此边了.
所以,把所有的边的权值加起来就行了.
因为所有点到最后都需要拆,则拆点的过程会涉及每一条边。因此我们对每一条边,取该边的两个顶点中权值较小的作为拆除该边的代价,最终统计拆除所有边所需要的最小代价和,即为所求解。
经验一:不要高估二星题.想的复杂了,必然不对.
经验二:换个角度看问题,别有洞天.
经验三:对过程寻求另一种描述.当我们把注意力放在点上的时候,谁能想到突破口在边上.
#include<stdio.h> #include<string.h> int a[10000]; int c[10000]; char b[10000][10000]; void main(){ int n, m; int i,j, x,y; int min; int ans = 0; int size; //freopen("in.txt", "r", stdin); memset(b, 0, sizeof(b)); memset(c, 0, sizeof(c)); scanf("%d%d", &n, &m); size = n; for (i = 0; i < n; i++) scanf("%d", &a[i]); for (i = 0; i < m; i++) { scanf("%d%d", &x, &y); x--; y--; b[x][y]=b[y][x] = 1; c[x] += a[y]; c[y] += a[x]; } min = 0; for (i = 1; i < n;i++) if (c[i] <= c[min]&&a[i]>a[min])min = i; again:ans += c[min]; j = min; c[j] = -1; for (i = 0; c[i] < 0; i++); min = i; for (; i < n; i++) { if (b[j][i] == 1&&c[i]>=0)c[i] -= a[j]; if (c[i] <= c[min]&&c[i]>=0&&a[i]>a[min])min = i; } size--; if(size>0)goto again; printf("%d", ans); }