算法-连通core的电阻-dfs

题意

芯片二维平面图,是一个n*n的二维矩阵(0<n<300),其中:
0表示core
-1表示不通的路径
其他正数表示电阻
需要连线将所有core连通(非俩俩相连),并且使路径电阻最小。
路径电阻计算方式为路径上最大的电阻值。

----
01430
343-1
71-1-1
3010

上面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;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值