灯泡游戏 (Kruskal)(并查集)

灯泡游戏

时间限制: 1 Sec  内存限制: 64 MB
提交: 9  解决: 4
[提交][状态][讨论版]

题目描述

有 一个n行m列的矩阵,左上角坐标是(0,0),右下角坐标是(n-1,m-1)。每个格子有一个字符, “0”至“9”表示数字0至9,“a”至“z”表示数字10至35,“A”至“Z”表示数字36至61。矩阵的每个格子都有一个灯泡,刚开始除了左上角的 灯泡是亮的,其他的灯泡都是灭的,刚开始你的得分是0。
游戏的过程是这样的:每次选一个灯泡是亮的格子X,同时选一个灯泡是灭的格子Y,而且要求 格子X和格子Y是相邻的格子。这个步骤会使你的得分增加,增加的值是:格子X与格子Y代表的数字的差的绝对值。当然,这个步骤会使你选中的灭的灯泡变亮。 重复上述操作,直到所有的灯泡都变成亮的。问你的最大得分是多少?

输入

第1行:两个整数n和m(1≤n,m≤50)。
  接下来是n行m列的矩阵。
  

输出

  一个整数,表示最大得分。

样例输入

2  2
05
aB

样例输出

69

提示

第一次选择:格子(0,0)和格子(1,0),得分是10-0=10;第二次选择:格子(1,0)和格子(1,1),得分是37-10:27;第三次选择:格子(1,1)和格子(0,1),得分是37-5=32,因此总得分是:10+27+32=69。

【分析】又学到了一种算法,求最小生成树的,当然这题是最大生成树,我用的是Kruskal算法,下面是AC代码。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <list>
#define mod 1000000007
#define inf 0x3f3f3f3f
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int N=2859;
int n,m,k,cnt=-1;
int d[4][2]= {0,1,1,0,0,-1,-1,0};
int parent[N+60];
bool vis[N][N];
char mp[55][55];
struct man {
    int u,v,w;
} edg[N];//结构体存节点与节点之间的权值
bool cmp(man g,man h) {
    return g.w>h.w;
}//将权值即得分从大到小排序
int change(char ch) {
    if(ch>='0'&&ch<='9')return (ch-'0');
    else if(ch>='a'&&ch<='z')return (ch-87);
    else return (ch-29);
}//字符转为数字
void init() {
    for(int i=0; i<=2855; i++)parent[i]=i;
}//初始化
int Find(int x) {
    if(parent[x] != x) parent[x] = Find(parent[x]);
    return parent[x];
}//查找并返回节点x所属集合的根节点
void Union(int x,int y) {
    x = Find(x);
    y = Find(y);
    if(x == y) return;
    parent[y] = x;
}//将两个不同集合的元素进行合并
void Build()//将矩形图转化为树
{
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            for(int k=0; k<4; k++) {
                int xx=i+d[k][0],yy=j+d[k][1];
                if(xx>=0&&xx<n&&yy>=0&&yy<m) {
                    int I=i*55+j,J=xx*55+yy;
                    if(!vis[I][J]) {
                        edg[++cnt].u=I;
                        edg[cnt].v=J;
                        edg[cnt].w=abs(change(mp[i][j])-change(mp[xx][yy]));
                        vis[I][J]=true;
                    }
                }
            }
        }
    }
}
void Kruskal() {
    int sumweight=0;//生成树的总权值,即所得分数
    int num=0;//已经选用边的数目
    int u,v;//顶点
    init();
    for(int i=0; i<=cnt; i++) {
        u=edg[i].u;
        v=edg[i].v;
        if(Find(u)!=Find(v)) {
            sumweight+=edg[i].w;
            num++;
            Union(u,v);
        }
        if(num>=n*m-1) break;//边已经全部建好
    }
    printf("%d
",sumweight);
}
int main() {
    memset(vis,false,sizeof(vis));
    int u,v,w;
    cin>>n>>m;
    for(int i=0; i<n; i++)cin>>mp[i];
    Build();
    sort(edg,edg+cnt+1,cmp);
    Kruskal();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/jianrenfang/p/5725205.html