拆点问题

题目详情

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);
}        



原文地址:https://www.cnblogs.com/weiyinfu/p/5013913.html