图及其他

深度优先

例题 
求连通性
给定一个方阵,定义连通:上下左右相邻,并且值相同。
可以想象成一张地图,不同的区域被涂以不同颜色。
输入:
整数N, (N<50)表示矩阵的行列数
接下来N行,每行N个字符,代表方阵中的元素
接下来一个整数M,(M<1000)表示询问数
接下来M行,每行代表一个询问,
格式为4个整数,y1,x1,y2,x2,
表示(第y1行,第x1列) 与 (第y2行,第x2列) 是否连通。
连通输出true,否则false

例如:
10
0010000000
0011100000
0000111110
0001100010
1111010010
0000010010
0000010011
0111111000
0000010000
0000000000
3
0 0 9 9
0 2 6 8
4 4 4 6

程序应该输出:
false
true
true
package 图及其他;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class 求连通性
{
	static int N;
	static int M;
	static int arr[][];
	static boolean flag=true;
	public static void main(String[] args) throws NumberFormatException, IOException
	{
		BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
		N=Integer.parseInt(bufferedReader.readLine());
		arr=new int [N+2][N+2];
		for (int i = 0; i < arr.length; i++)
		{
			arr[i][0]=100;
			arr[0][i]=100;
			arr[arr.length-1][i]=100;
			arr[i][arr.length-1]=100;
		}
		
		
		for (int i = 1; i <= N; i++)
		{
			String string=bufferedReader.readLine();
			for (int j = 0; j < string.length(); j++)
			{
				if (string.charAt(j)=='1')
				{
					arr[i][j+1]=1;
				}
			}
		}
		
//		for (int i = 0; i < arr.length; i++)
//		{
//			System.out.println(Arrays.toString(arr[i]));
//		}
		
		M=Integer.parseInt(bufferedReader.readLine());
		
		for (int i = 0; i < M; i++)
		{
			
			String []strings=bufferedReader.readLine().split(" ");
			int x1=Integer.parseInt(strings[0])+1;
			int y1=Integer.parseInt(strings[1])+1;
			int x2=Integer.parseInt(strings[2])+1;
			int y2=Integer.parseInt(strings[3])+1;
			dfs(x1, y1, x2, y2);
			if (flag)
			{
				System.out.println(false);
			}
			flag=true;
		}
	}
	private static void dfs(int x, int y, int x2, int y2)
	{
		if (flag==false)
		{
			return;
		}
		if (x>=N+1||y>=N+1||x<=0||y<=0)
		{
			return ;
		}
		if (x==x2&&y==y2)
		{
			System.out.println("true");
			flag=false;
			return;
		}
		int old=arr[x][y];
		arr[x][y]=3;//探索过
		if (arr[x][y+1]==old)
		{
			dfs(x, y+1, x2, y2);
		}
		if (arr[x+1][y]==old)
		{
			dfs(x+1, y, x2, y2);
		}
		if (arr[x-1][y]==old)
		{
			dfs(x-1, y, x2, y2);
		}
		if (arr[x][y-1]==old)
		{
			dfs(x, y-1, x2, y2);
		}
		arr[x][y]=old;
	}

}
class Point {
	int x;
	int y;
	public Point(int x, int y)
	{
		super();
		this.x = x;
		this.y = y;
	}
	
}
广度优先
节点动态生成的
分酒问题
有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升
开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着。

允许把酒从一个瓶子倒入另一个瓶子,但只能把一个瓶子倒满或把一个瓶子倒空,不能有中间状态。
这样的一次倒酒动作称为1次操作。

假设瓶子的容量和初始状态不变,对于给定的目标状态,至少需要多少次操作才能实现?
本题就是要求你编程实现最小操作次数的计算。

输入:最终状态(空格分隔)
输出:最小操作次数(如无法实现,则输出-1)

例如:
输入:
9 0 0 0
应该输出:
0

输入:
6 0 0 3
应该输出:
-1

输入:
7 2 0 0
应该输出:
2

package 图及其他;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.TreeSet;

public class 分酒问题
{

	public static void main(String[] args) throws IOException
	{	
		BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
		String from="9 0 0 0";
		TreeSet<String> froms=new TreeSet<>();
		TreeSet<String> hists=new TreeSet<>();
		froms.add(from);
		hists.add(from);
		String goal=bufferedReader.readLine();
		System.out.println(bfs(hists,froms,goal));
	}

	private static int bfs(TreeSet<String> hists, TreeSet<String> froms, String goal)
	{
		//已经找到了
		if (froms.contains(goal))
		{
			return 0;
		}
		
		TreeSet<String> set=new TreeSet<>();
		
		for (String string : froms)
		{
			TreeSet<String> treeSet=move(string);
			set.addAll(treeSet);
		}
		
		//已经访问过的不再访问
		set.removeAll(hists);
		
		if (set.isEmpty())
		{
			return -1;
		}
		
		
		
		//加入访问过后
		hists.addAll(set);
		
		int r=bfs(hists, set, goal);
		if (r<0)
		{
			return r;
		}
		return r+1;
	}

	private static TreeSet<String> move(String string)
	{
		//容量
		final int []cap={9,7,4,2};
		
		//各种子走法
		TreeSet<String >treeSet=new TreeSet<>();
		
		//各瓶子状态
		String []strings=string.split(" ");
		int []data=new int [strings.length];
		for (int i = 0; i < strings.length; i++)
		{
			data[i]=Integer.parseInt(strings[i]);
		}
		
		//from
		for (int i = 0; i < data.length; i++)
		{
			//to
			for (int j = 0; j < data.length; j++)
			{
				//自己倒向自己
				if (i==j)
				{
					continue;
				}
				//from瓶子空的
				if (cap[i]==0)
				{
					continue;
				}
				//to瓶子满的
				if (data[j]==cap[j])
				{
					continue;
				}
				
				//倒后的容量
				int vi = 0;
				int vj = 0;
				//全倒
				if (cap[j]-data[j]>=data[i])
				{
					vj=data[i]+data[j];
					vi=0;
				}
				//倒得会多
				if (cap[j]-data[j]<data[i])
				{
					vj=cap[j];
					vi=data[i]-(cap[j]-data[j]);
				}
				
				
				StringBuffer stringBuffer=new StringBuffer();
				for (int k = 0; k < data.length; k++)
				{
					if (k==i)
					{
						stringBuffer.append(vi+" ");
					}
					else if (k==j)
					{
						stringBuffer.append(vj+" ");
					}
					else 
					{
						stringBuffer.append(data[k]+" ");
					}
				}
				treeSet.add(stringBuffer.toString().trim());
				
			}
			
		}
		
		return treeSet;
	}

}

生成树

package 洛谷;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Prime算法思路:
 * 不断从没有连接的节点(V-U)中选择权值最小的加入已联通的(U),加入后修改(V-U)与(U)的权值
 * 加入当(V-U)=空集时候树生成完毕
 * @author 零
 *
 */
public class P3366最小生成树
{
	static int n;
	static int m;
	public static void main(String[] args) throws IOException
	{
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
		String[] strings = bufferedReader.readLine().trim().split(" ");
		n = Integer.parseInt(strings[0]);// 节点数目
		m = Integer.parseInt(strings[1]);// 边数目
		int arr[][] = new int[n][n];
		ArrayList<Integer> arrayList = new ArrayList<>();
		for (int i = 0; i < arr.length; i++)
		{
			for (int j = 0; j < arr[i].length; j++)
			{
				arr[i][j] = Integer.MAX_VALUE;
			}
			arrayList.add(i + 1);
		}
		Group group = new Group(arrayList, arr);

		for (int i = 0; i < m; i++)
		{
			strings = bufferedReader.readLine().trim().split(" ");
			int v1 = Integer.parseInt(strings[0]);// v1
			int v2 = Integer.parseInt(strings[1]);// v2
			int power = Integer.parseInt(strings[2]);// 权值
			// 避免重边 无向图重边取短的
			if (group.group[v1 - 1][v2 - 1] > power)
			{
				group.group[v1 - 1][v2 - 1] = power;
				group.group[v2 - 1][v1 - 1] = power;
			}
		}
	
		SpanTree[] spanTrees = new SpanTree[n];
		
		for (int i = 0; i < spanTrees.length; i++)
		{
			spanTrees[i] = new SpanTree();
		}
		prim(group, spanTrees);

		int sum=0;
		for (int i = 0; i < spanTrees.length; i++)
		{
			System.out.println(spanTrees[i]);
			sum+=spanTrees[i].weight;
		}
		System.out.println(sum);
	}

	private static void prim(Group group, SpanTree[] spanTrees)
	{

		int n = group.list.size();
		int lowCount[] = new int[n];
		for (int i = 0; i < lowCount.length; i++)
		{
			lowCount[i] = group.group[0][i];// 初始化lowCount数组
		}

		lowCount[0] = -1;//将第一个节点加入U
		spanTrees[0].index = group.list.get(0);

		int mincount = 0, k = 0;
		for (int i = 1; i < n; i++)
		{
			mincount = Integer.MAX_VALUE;
			for (int j = 0; j < n; j++)
			{
				// 找出(V-U)中距离(U)最近的
				// 最近的 (V-U)中
				if (lowCount[j] < mincount && lowCount[j] > 0)
				{
					mincount = lowCount[j];
					k = j;
				}

			}
			// 将最近的加入(V-U)中
			lowCount[k] = -1;
			spanTrees[i].index = group.list.get(i);
			spanTrees[i].weight = mincount;

			// 根据加入的k 修改找出(V-U)中距离(U)的值
			for (int j = 0; j < n; j++)
			{
				// 如果这个节点 K 到其它节点(V-U)比原来U到(V-U)的要小 那么就修改
				if (group.group[k][j] < lowCount[j])
				{
					lowCount[j] = group.group[k][j];
				}
			}
		}
	}

}

class Group
{
	List<Integer> list;
	int group[][];

	public Group(List<Integer> list, int[][] group)
	{
		super();
		this.list = list;
		this.group = group;
	}

	@Override
	public String toString()
	{
		return "GroupPoint [" + (list != null ? "list=" + list + ", " : "")
				+ (group != null ? "group=" + Arrays.toString(group) : "") + "]";
	}

	public Group()
	{
		super();
		// TODO Auto-generated constructor stub
	}
}

class SpanTree
{
	int index;
	int weight;

	public SpanTree(int index, int weight)
	{
		super();
		this.index = index;
		this.weight = weight;
	}

	public SpanTree()
	{
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString()
	{
		return "SpanTree [index=" + index + ", weight=" + weight + "]";
	}

}
最后扯一句,为什么这道题我不去AC呢,128MB的空间限制就算我换成邻接表的物理结构也会内存不够的。正规Java算法基本会给256MB以上的内存


7.3 生成树 把图变成树,树是没有环的
风险系数
标题:风险度量

X星系的的防卫体系包含 n 个空间站。这 n 个空间站间有 m 条通信链路,构成通信网。
两个空间站间可能直接通信,也可能通过其它空间站中转。

对于两个站点x和y (x != y), 如果能找到一个站点z,使得:
当z被破坏后,x和y不连通,则称z为关于x,y的关键站点。

显然,对于给定的两个站点,关于它们的关键点的个数越多,通信风险越大。


你的任务是:已经网络结构,求两站点之间的通信风险度,即:它们之间的关键点的个数。


输入数据第一行包含2个整数n,m(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,链路数。
空间站的编号从1到n。通信链路用其两端的站点编号表示。
接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条链路。
最后1行,两个数u,v,代表被询问通信风险度的两个站点。

输出:一个整数,如果询问的两点不连通则输出-1.


例如:
用户输入:
7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6
则程序应该输出:
2
生成了树后 y的所有祖先都可能是割点(割掉以后不连通,候选集合)
如果此候选点为根的子树有孩子返祖超过了当前候选点的高度,那么就不是敏感的割点
最高反祖:以我当前节点为根的子树家族的所有节点,返回到最祖先的那个节点返祖值(最高的)
package 图及其他;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class 生成树风险系数
{

	static final int VISED=0;
	static final int DEEP=1;
	static final int FATHER=2;
	static final int HIGH_ORIGIN=3;
	public static void main(String[] args) throws NumberFormatException, IOException
	{
		BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
		
		String []strings=bufferedReader.readLine().trim().split(" ");
		//节点数
		int n=Integer.parseInt(strings[0]);
		//边数
		int m=Integer.parseInt(strings[1]);
		
		List<Integer>[] list=new List[m+1];//每个顶点->其他顶点
		for (int i = 0; i < list.length; i++)
		{
			list[i]=new ArrayList<>();
		}
		
		int [][]resultTree=new int[m+1][4];//生成树结果   1.访问过 1/0 	2.深度    3.父节点  4.最高反祖
		
		//input
		for (int i = 0; i < m; i++)
		{
			strings=bufferedReader.readLine().trim().split(" ");
			int v1=Integer.parseInt(strings[0]);
			int v2=Integer.parseInt(strings[1]);
			list[v1].add(v2);
			list[v2].add(v1);
		}
		
		strings=bufferedReader.readLine().split(" ");
		int a=Integer.parseInt(strings[0]);
		int b=Integer.parseInt(strings[1]);
		resultTree[a][VISED]=1;//访问过
		resultTree[a][DEEP]=0;//深度
		resultTree[a][FATHER]=0;//父节点
		resultTree[a][HIGH_ORIGIN]=0;//最高反祖
		
		dfs(list,resultTree,a,b);
		
		System.out.println(solve(resultTree,a,b));
	}

	/**
	 * 
	 * @param resultTree 生成树
	 * @param root 根
	 * @param leaf 叶子
	 * @return
	 */
	private static int solve(int[][] resultTree, int root, int leaf)
	{
		int sum=0;
		int p=leaf;
		int high_fan_zu=resultTree[p][HIGH_ORIGIN];//当前最高(小)反祖
		while(true)
		{
			int pa=resultTree[p][FATHER];
			System.out.println("pa "+pa);
			//达到了root
			if (pa==0||pa==root)
			{
				break;
			}
			//父节点深度小于high_fan_zu
			//说明当前已知 还没有高过这个父节点 如果我删掉 下面的孩子就连不上了
			//说明是合格的割点
			if (resultTree[pa][DEEP]<=high_fan_zu)
			{
				System.out.println("gedian pa "+pa);
				sum++;
			}
			//pa的反祖级更高
			if (resultTree[pa][HIGH_ORIGIN]<high_fan_zu)
			{
				high_fan_zu=resultTree[pa][HIGH_ORIGIN];
			}
			p=pa;
		}	
		return sum;
	}

	/**
	 * dfs生成树
	 * @param list 那个节点连接的那个节点
	 * @param resultTree 生成树
	 * @param a 当前的根
	 * @param b 目标的目标
	 */
	private static void dfs(List<Integer>[] list, int[][] resultTree, int v1, int v2)
	{
		if (v1==v2)
		{
			return ;
		}
		
		//遍历所有与v1连着的节点
		for (Integer is : list[v1])
		{
			//遍历过程中碰到了访问过的节点  就做返祖
			if (resultTree[is][VISED]>0)
			{
				//试图找孩子的时候装上了祖先	
				fan_zu(resultTree,v1,resultTree[is][DEEP]);
				continue;
			}
			
			resultTree[is][VISED]=1;//标记为访问过
			resultTree[is][DEEP]=resultTree[v1][DEEP]+1;//深度+1
			resultTree[is][FATHER]=v1;//父节点
			resultTree[is][HIGH_ORIGIN]=resultTree[v1][DEEP];//刚加入进来返祖为父节点
			dfs(list, resultTree, is, v2);
		}
	}

	/**
	 * 返祖
	 * @param resultTree 生成树结果
	 * @param me 当前找孩子节点碰到已经访问过了的
	 * @param goal 访问过的目标深度
	 */
	private static void fan_zu(int[][] resultTree, int me, int goal)
	{
		//此节点深度没有最大返祖大
		if (resultTree[me][HIGH_ORIGIN]<=goal)
		{
			return;
		}
		//改变返祖
		resultTree[me][HIGH_ORIGIN]=goal;
		//继续让父节点更新返祖值  将祖先返祖
		fan_zu(resultTree, resultTree[me][FATHER], goal);
	}

}



  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值