灯泡游戏
时间限制: 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代码。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#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\n",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; }