题意
芯片二维平面图,是一个n*n的二维矩阵(0<n<300),其中:
0表示core
-1表示不通的路径
其他正数表示电阻
需要连线将所有core连通(非俩俩相连),并且使路径电阻最小。
路径电阻计算方式为路径上最大的电阻值。
- | - | - | - |
---|---|---|---|
0 | 14 | 3 | 0 |
3 | 4 | 3 | -1 |
7 | 1 | -1 | -1 |
3 | 0 | 1 | 0 |
上面4*4矩阵中的加粗数字,将4个core(0)连通。路径电阻为4。
0<n<300
core数量小于100,大于0
样例
输入:
3 (n=3)
0 1 3
-1 2 1
0 1 1
输出:
2
分析
算出每个core到core0的电阻,取最大值,即可。
题解1:
递归函数,自顶向下
题解2:
递归函数,自底向上,9万次调用,栈溢出
非典型的DAG图,DP方式,从头至尾一遍刷表搞不定
所以用队列存储更新的节点,逐个刷新受影响的节点,直至收敛。
题解3:
DP刷表方式,自底向上
参考题解1
#include<stdio.h>
#define MIN(a,b) (a)<(b)?(a):(b)
#define MAX(a,b) (a)>(b)?(a):(b)
int N, coreNum; //矩阵范围N,核心数coreNum
int chip[300][300];
int core[100][2]; //记录核心的坐标
int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1} };
//坐标xy到核心0的路径电阻。4代表从哪个方向递推来的
//因为有时候visit标记导致某个方向continue了
//所以必须要4个方向都递推过,坐标xy的路径电阻才能确认,然后才能使用memo
int memo[300][300][4] = {0};
bool visit[300][300] = { 0 };
void input();
bool illeagle(int x, int y)
{
if (x < 0 || y < 0)
return false;
if(x > N-1 || y > N - 1)
return false;
if (chip[x][y] == -1)
return false;
return true;
}
int dfs(int x, int y) //坐标xy到核心0的最小路径电阻
{
int i, nextx, nexty;
int tmp = INT_MAX;
if (core[0][0] == x && core[0][1] == y)
return 0;
if (memo[x][y][0] != 0 &&
memo[x][y][1] != 0 &&
memo[x][y][2] != 0 &&
memo[x][y][3] != 0)
{
//4个中最小的
return MIN(MIN(MIN(memo[x][y][0], memo[x][y][1]), memo[x][y][2]), memo[x][y][3]);
}
for (i = 0; i < 4; i++)
{
nextx = x + dir[i][0];
nexty = y + dir[i][1];
if (memo[nextx][nexty][i] != 0)
{
tmp = MIN(tmp, memo[nextx][nexty][i]);
continue;
}
if (false==illeagle(nextx, nexty))
{
memo[nextx][nexty][i] = INT_MAX;
continue;
}
if (visit[nextx][nexty] == true)
continue;
visit[nextx][nexty] = true;
memo[nextx][nexty][i] = dfs(nextx, nexty);
visit[nextx][nexty] = false;
tmp = MIN(tmp, dfs(nextx, nexty));
}
return MAX(tmp, chip[x][y]);
}
/*
3
0 1 3
-1 2 1
0 1 1
5
0 1 1 3 2
-1 -1 2 0 6
-1 -1 5 1 7
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
*/
int main()
{
int i, j;
int res = 0;
input();
for (i = 1; i < coreNum; i++) //计算每个core到0号core的电阻,取最大值
{
visit[core[i][0]][core[i][1]] = true;
res = MAX(res, dfs(core[i][0], core[i][1]));
visit[core[i][0]][core[i][1]] = false;
}
}
void input()
{
int i = 0; int j = 0;
coreNum = 0;
scanf("%d", &N);
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
scanf("%d", &chip[i][j]);
if (chip[i][j] == 0)
{
core[coreNum][0] = i;
core[coreNum][1] = j;
coreNum++;
}
}
}
}
参考题解2
#include<stdio.h>
#define MIN(a,b) (a)<(b)?(a):(b)
#define MAX(a,b) (a)>(b)?(a):(b)
int N, coreNum; //矩阵范围N,核心数coreNum
int chip[300][300];
int core[100][2]; //记录核心的坐标
int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1} };
int memo[300][300] = {0};
void input();
bool illeagle(int x, int y)
{
if (x < 0 || y < 0)
return false;
if(x > N-1 || y > N - 1)
return false;
if (chip[x][y] == -1)
return false;
return true;
}
void updated(int x, int y)
{
int i = 0;
int nextx, nexty;
int memonext;
for (i=0;i<4;i++) //xy更新了,遍历xy四周的4个点尝试被更新
{
nextx = x + dir[i][0];
nexty = y + dir[i][1];
if (true==illeagle(nextx, nexty))
{
//nextxy 到core0的路径值比xy大,可以更新一下。
//更新为chip[nextx][nexty]或者memo[x][y]。
memonext = MAX(MIN(memo[nextx][nexty], memo[x][y]), chip[nextx][nexty]);
if (memonext!= memo[nextx][nexty])
{
memo[nextx][nexty] = memonext;
updated(nextx, nexty);//nextx真的更新了,就要更新nextx周围的点
}
}
}
}
/*
3
0 1 3
-1 2 1
0 1 1
5
0 1 1 3 2
-1 -1 2 0 6
-1 -1 5 1 7
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
*/
int main()
{
int i, j;
int res = 0;
input();
memo[core[0][0]][core[0][1]] = 0;
updated(core[0][0], core[0][1]); //0号core的坐标开始更新,影响4周的4个点
}
void input()
{
int i = 0; int j = 0;
coreNum = 0;
scanf("%d", &N);
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
scanf("%d", &chip[i][j]);
if (chip[i][j] == 0)
{
core[coreNum][0] = i;
core[coreNum][1] = j;
coreNum++;
}
memo[i][j] = INT_MAX;
}
}
}
参考题解3
#include<stdio.h>
#define MIN(a,b) (a)<(b)?(a):(b)
#define MAX(a,b) (a)>(b)?(a):(b)
int N, coreNum; //矩阵范围N,核心数coreNum
int chip[300][300];
int core[100][2]; //记录核心的坐标
int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1} };
int memo[300][300] = {0};
#define quelen (300*300)
int queue[quelen] = {0};
bool inqueue[300][300] = { 0 };
void input();
bool illeagle(int x, int y)
{
if (x < 0 || y < 0)
return false;
if(x > N-1 || y > N - 1)
return false;
if (chip[x][y] == -1)
return false;
return true;
}
/*
3
0 1 3
-1 2 1
0 1 1
5
0 1 1 3 2
-1 -1 2 0 6
-1 -1 5 1 7
-1 -1 -1 -1 -1
-1 -1 -1 -1 -1
10
12 5 7 3 4 13 1 5 10 17 13 19 18 5 2 9 11 7 15 15 11 16 20 12 8 4 1 9 16 13 7 5 14 12 16 0 11 10 16 19 2 1 11 4 19 7 12 12 1 9 10 3 15 0 14 19 4 18 12 14 20 6 7 9 3 12 4 11 15 20 1 1 6 6 2 14 6 7 4 17 16 13 12 4 13 4 5 3 19 13 6 12 3 5 0 12 0 10 20 17
*/
int main()
{
int i, j;
int head = 0;
int tail = 0;
int x, y;
int nextx, nexty, memonext;
int tmp;
input();
memo[core[0][0]][core[0][1]] = 0;
//从core0开始,core0坐标放入队列
queue[tail++] = core[0][0] * N + core[0][1];
inqueue[core[0][0]][core[0][1]] = true;
while (tail!=head) //队列非空,有memo更新了,尝试更新周围的点
{
tmp = queue[head++]; //取出一个点
head = head % quelen;
x = tmp / N;
y = tmp % N;
inqueue[x][y] = false;
for (i = 0; i < 4; i++) //更新这个点四周的点
{
nextx = x + dir[i][0];
nexty = y + dir[i][1];
if (true == illeagle(nextx, nexty))
{
memonext = MAX(MIN(memo[nextx][nexty], memo[x][y]), chip[nextx][nexty]);
if (memonext != memo[nextx][nexty])
{
memo[nextx][nexty] = memonext;
//nextx更新了,把它放到队列
if (inqueue[nextx][nexty] == false)
{
queue[tail++] = nextx * N + nexty;
tail = tail % quelen;
inqueue[nextx][nexty] = true;
}
}
}
}
}
}
void input()
{
int i = 0; int j = 0;
coreNum = 0;
scanf("%d", &N);
//N = 299;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
//chip[i][j] = rand() % 10;
//if (chip[i][j] > 8 || (chip[i][j]==0 && coreNum==100))
// chip[i][j] = -1;
scanf("%d", &chip[i][j]);
if (chip[i][j] == 0)
{
core[coreNum][0] = i;
core[coreNum][1] = j;
coreNum++;
}
memo[i][j] = INT_MAX;
inqueue[i][j] = false;
}
}
}