- 题目大意
给你一个r * c的格子,每个格子有一个 ‘ ’ 或者 ‘/’ 的墙,以及打掉墙的费用,问使得所有块联通的最小费用。(自己可以配合图来看好理解一点)
- 解题思路
我们可以将其转化成联通块的问题,就是把每个格子看成两部分,左侧和右侧。以一行来看,假设两个格子A,B。那么B格子的右侧的编号一定和A格子的左侧的编号相同。给每个格子的左右侧标上号,然后加入边,边的两个端点为一个格子的两个编号。权值为墙的费用然后处理行与行之间的边,然后假设上边格子为A,下面格子为B。那么如果A是‘/’,B是’/’,那么A的右格子和B的左格子是相通的,这时候加一条边,将权值设为0就可以了。(注意位置和数组大小!!!)
- 代码
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<string>
using namespace std;
const int MAX = 1e6;
int fa[MAX];
string str[400];
int num[150][150];
int sum,sum1,cnt;
void init()
{
for (int i = 1; i <= MAX; i++)
{
fa[i] = i;
}
cnt=0;
}
struct Edge {
int u, v, w;
bool operator<(const Edge &rhs)const {
return w < rhs.w;
}
}e[MAX];
struct Edge2{
int u,v;
}maps[150][150];
void addEdge(int u,int v,int w)
{
e[cnt].v=v;
e[cnt].u=u;
e[cnt++].w=w;
}
int find(int x)
{
if (x == fa[x])
return x;
else
return fa[x] = find(fa[x]);
}
bool Union(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx == fy)
return false;
fa[fx] = fy;
return true;
}
void kruskal(int m)
{
sort(e, e + m);
for (int i = 0; i < m; i++)
{
int u = e[i].u, v = e[i].v, w = e[i].w;
if (Union(u, v))
{
sum +=w;
}
}
}
int main()
{
int t,n, m;
scanf("%d",&t);
for(int q=1;q<=t;q++)
{
scanf("%d%d",&n,&m);
init();
for(int i=0;i<n;i++)
{
cin>>str[i];
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%d",&num[i][j]);
}
}
int p = 1;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (j == 0)
{
maps[i][j].u = p++;
maps[i][j].v = p++;
addEdge(maps[i][j].u, maps[i][j].v, num[i][j]);
}
else
{
maps[i][j].u = maps[i][j - 1].v;
maps[i][j].v = p++;
addEdge(maps[i][j].u, maps[i][j].v, num[i][j]);
}
if (i != 0)
{
if (str[i - 1][j] == '/')
{
if (str[i][j] == '/')
{
addEdge(maps[i][j].u, maps[i - 1][j].v, 0);
}
else
{
addEdge(maps[i][j].v, maps[i - 1][j].v, 0);
}
}
else
{
if (str[i][j] == '/')
{
addEdge(maps[i][j].u, maps[i - 1][j].u, 0);
}
else
{
addEdge(maps[i][j].v, maps[i - 1][j].u, 0);
}
}
}
}
}
sum=0;
kruskal(cnt);
printf("Case %d: %d
",q,sum);
}
return 0;
}