当前位置: 代码迷 >> 综合 >> POJ 1192 最优连通子集
  详细解决方案

POJ 1192 最优连通子集

热度:7   发布时间:2023-12-16 05:59:46.0

题目

众所周知,我们可以通过直角坐标系把平面上的任何一个点P用一个有序数对(x, y)来唯一表示,如果x, y都是整数,我们就把点P称为整点,否则点P称为非整点。我们把平面上所有整点构成的集合记为W。
定义1 两个整点P1(x1, y1), P2(x2, y2),若|x1-x2| + |y1-y2| = 1,则称P1, P2相邻,记作P1~P2,否则称P1, P2不相邻。
定义 2 设点集S是W的一个有限子集,即S = {P1, P2,…, Pn}(n >= 1),其中Pi(1 <= i <= n)属于W,我们把S称为整点集。
定义 3 设S是一个整点集,若点R, T属于S,且存在一个有限的点序列Q1, Q2, ?, Qk满足:

  1. Qi属于S(1 <= i <= k);
  2. Q1 = R, Qk = T;
  3. Qi~Qi + 1(1 <= i <= k-1),即Qi与Qi + 1相邻;
  4. 对于任何1 <= i < j <= k有Qi ≠ Qj;

我们则称点R与点T在整点集S上连通,把点序列Q1, Q2,…, Qk称为整点集S中连接点R与点T的一条道路。
定义4 若整点集V满足:对于V中的任何两个整点,V中有且仅有一条连接这两点的道路,则V称为单整点集。
定义5 对于平面上的每一个整点,我们可以赋予它一个整数,作为该点的权,于是我们把一个整点集中所有点的权的总和称为该整点集的权和。
我们希望对于给定的一个单整点集V,求出一个V的最优连通子集B,满足:

  1. B是V的子集
  2. 对于B中的任何两个整点,在B中连通;
  3. B是满足条件(1)和(2)的所有整点集中权和最大的。

输入

第1行是一个整数 N ( 2 ≤ N ≤ 1000 ) N(2 \le N \le 1000) N(2N1000),表示单整点集V中点的个数;
以下N行中,第i行 ( 1 ≤ i ≤ N ) (1 \le i \le N) (1iN)有三个整数,Xi, Yi, Ci依次表示第i个点的横坐标,纵坐标和权。同一行相邻两数之间用一个空格分隔。 ? 1 0 6 ≤ X i , Y i ≤ 1 0 6 ; ? 100 ≤ C i ≤ 100 -10^6 \le Xi, Yi \le 10^6;-100 \le Ci \le 100 ?106Xi,Yi106?100Ci100

输出

仅一个整数,表示所求最优连通集的权和。

样例输入

5
0 0 -2
0 1 1
1 0 1
0 -1 1
-1 0 1

样例输出

2

题目分析

首先,本题在对数据的存储上,我采用了链式前项星的办法。
对于链式前项星,它是一种以边为存储对象的数据结构,时间复杂度为O(n),空间复杂度为O(n),遍历复杂度为O(n)。通常以两个数组实现:Edge edge[]和int head[];
通常的结构体数组edge的结构为

struct Edge
{
    int to;//边的指向点int w;//边的权值int next;//改起点的上一条边的编号
}edge[MAXN];//边集,下标即边的标号

head数组则是统计了每一个点周围所有边的最后一条边的编号。通过head数组提供的边位置,可以循环将整张图搜索完毕。

其次,本题在对数据的处理上,采用了树形DP的方法,给每一个结点两个空间,一个用于存储计算当前结点权值后的总权值,另一用于存储未计算当前结点权值的总权值(即保留各个子树中权值最大的)。然后比较,层层递归即可。

代码

#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;struct Node{
    int next;int to;
};int head[1005];
Node edge[2010];
int weight[1005];//各节点的权值
int px[1005];//位置
int py[1005];
int vis[1005];//是否已经扫描过
int dp[1005][2];//统计当前结点最大权值
int edge_n = 1;void add(int i,int j){
    //链式前向星edge[edge_n].next = head[i];//指向该点的前一条边edge[edge_n].to = j;//边的终点head[i] = edge_n++;//head更新最后一条边的编号edge[edge_n].next = head[j];//无向图,双向存储edge[edge_n].to = i;head[j] = edge_n++;
}void T_dp(int pos){
    //Tree_dpvis[pos] = 1;dp[pos][1] = weight[pos];for(int i = head[pos];i!=0;i = edge[i].next){
    int t = edge[i].to;//下一个结点if(vis[t]==0){
    T_dp(t);if(dp[t][1]>0)//非负就加上该结点dp[pos][1]+=dp[t][1];dp[pos][0] = max(dp[pos][0],max(dp[t][0],dp[t][1]));//比较该结点下各子树最大的权值,并赋予给当前结点}}
}int main(){
    int n,x,y,w;cin >> n;for(int i = 1;i<=n;++i)scanf("%d %d %d",&px[i],&py[i],&weight[i]);for(int i = 1;i<n;++i)for(int j = i+1;j<=n;++j)if(abs(px[i]-px[j])+abs(py[i]-py[j])==1)//判断范围add(i,j);T_dp(1);//设1为根节点cout << max(dp[1][0],dp[1][1]) << endl;return 0;
}

运行结果

在这里插入图片描述