Java算法练习题

目录

有效的数独

单词拆分  动态规划题型

dp难点

如果想要将列表中的所有字符串连接成一个单一的字符串,可以使用stream().collect()和joining()方法

获取列表中的第一个字符串


有效的数独

public class IsShuDu {
    public boolean isValiduku(char[][] board){
        boolean[][] rowUsed = new boolean[9][9];
        boolean[][] colUsed = new boolean[9][9];
        boolean[][] boxUsed = new boolean[9][9];
        for(int i = 0 ; i < 9 ; i++){
            for(int j = 0 ; j < 9 ; j++){
                char num = board[i][j];
                if(num != '.'){   /* '.'表示空格,这里是表示当前位置有位置 , 则将字符num转换为数字digit*/
                    int digit = num -'1';
//                    检查数字在当前行、当前列和当前3*3行宫是否已经出现过,如果已经出现过,说明数独无效,返回false
                    if(rowUsed[i][digit] || colUsed[j][digit] || boxUsed[i/3*3+j/3][digit])
                    {
//                        如果已经出现过,则设为false
                        return false;
                    }
//                    未出现过则设为false,然后继续遍历下一个为止
                    rowUsed[i][digit] = colUsed[j][digit] = boxUsed[i/3*3+j/3][digit]=true;
                }
            }
        }
        return true;
    }
}
public class Main {
    public static void main(String[] args) {
        char[][] board = {
{'5','3','.','.','7','.','.','.','.'},
{'6','.','.','1','9','5','.','.','.'},
{'.','9','8','.','.','.','.','6','.'},
{'8','.','.','.','6','.','.','.','3'},
{'4','.','.','8','.','3','.','.','1'},
{'7','.','.','.','2','.','.','.','6'},
{'.','6','.','.','.','.','2','8','.'},
{'.','.','.','4','1','9','.','.','5'},
{'.','.','.','.','8','.','.','7','9'}
};
        IsShuDu isShuDu = new IsShuDu();
                System.out.printf("是否为数独:%b\t", isShuDu.isValiduku(board));
    }
}

返回值为:

是否为数独:true

关键词:关于字符的二维数组与赋值方法,字符减去ASCLL码的'1',二维数组判断方法与返回值false和true在不同的位置(行)时所对应的用法,返回值为布尔类型的接收打印的方法,要判断是否出现过的数字可以将这个数字放在二维数组的第二个括号里。

单词拆分  动态规划题型

简介:判断一个字符串能否由多个已知字符串组成

public class WordBreak {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;// 空字符串可以被拆分
        for (int i = 1; i <= s.length(); i++) {
            for (String word : wordDict) {
                int len = word.length();
//                如果i的前len个字符可以被拆分成单词,并且最后一个单词和word相等,则将dp[i]设为true
                if (i >= len && dp[i - len] && word.equals(s.substring(i - len, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}
public class Main {
    public static void main(String[] args) {
        String s = "leetcode";
        List<String> wordDict = new ArrayList<>();
        wordDict.add("leet");
        wordDict.add("code");
        WordBreak wordBreak=new WordBreak();
        System.out.printf("%b",wordBreak.wordBreak(s,wordDict));
    }
}

返回值为:

true

关键词:字符串列表与赋值方法,for的第二种使用方法

dp难点
if (i >= len && dp[i - len] && word.equals(s.substring(i - len, i))) {  
    dp[i] = true;  
    break;  
}

这里有几个关键点:

  1. i >= len:确保我们检查的子字符串长度至少和当前字典中的单词长度一样。
  2. dp[i - len]:这是一个动态规划的引用,表示从索引i-leni-1的子字符串是否可以被拆分成字典中的某个单词。
  3. word.equals(s.substring(i - len, i)):这是判断当前位置的子字符串是否与字典中的某个单词匹配。s.substring(i - len, i)获取的是从索引i-leni-1的子字符串,这与字典中的当前单词进行比较。

简单来说,这行代码的意思是:如果从当前位置开始的一个子字符串与字典中的一个单词匹配,并且这个子字符串前面的部分(长度为len)也可以被拆分成字典中的单词,那么就将dp[i]设为true,表示从索引0i的子字符串可以被拆分成字典中的单词。

拓展:

如果想要将列表中的所有字符串连接成一个单一的字符串,可以使用stream().collect()joining()方法
String combinedString = wordDict.stream().collect(Collectors.joining(", "));
获取列表中的第一个字符串
String value = wordDict.get(0);

Dijkstra算法  堆优化

Dijkstra算法以及堆优化的基本概念。Dijkstra算法是一种用于在有向图或无向图中查找从源顶点到其他所有顶点的最短路径的算法。而堆优化则是一种用于加速Dijkstra算法的技巧,通过使用优先队列来维护待处理的顶点。

P1907 设计道路

# 设计道路

## 题目描述

Caesar远征高卢回来后,对你大加赞赏,他亲自来到Genoa视察。

Genoa在你的建设下变得无比繁荣,由于财政收入的增加,你为城市修建了交通系统。古罗马的交通系统由两部分组成——Dirt Road和Rome Road。两个路口间只可能是其中一种道路。在Rome Road上可以驾驶马车,而在Dirt Road上则不行。由于修建道路是一项浩大的工程,使得你无法将整个城市用Rome Road连接起来。

现在Caesar已经到达码头,他要求去你家参观。Caesar由一个癖好,喜欢坐车而不喜欢走路。所以Caesar走Dirt Road时的不满值要比走Rome Road时大。

为了不让Caesar过于不满而罢免你的职位,请设计路线使得Caesar的不满值最小。

## 输入格式

输入数据第一行有两个实数,分别表示走Dirt Road和Rome Road一个单位长度时Caesar的不满值。接下来是一个整数N(N<=1000),代表路口总数。接下来有N行,每行一组实数(x,y)分别描述这N个路口的坐标。接下来有若干行,每行一组整数(i,j),表示第i个路口与第j个路口间为Rome Road,以0 0结束。最后两行,每行一对实数,分别描述码头和你家的坐标。

## 输出格式

输出Caesar从码头到你家的最小不满值,保留4位小数

## 样例 #1

### 样例输入的注释 #1
100.0 2.0         //分别表示走Dirt Road和Rome Road一个单位长度时Caesar的不满值。
2                      /*整数N(N<=1000),代表路口总数。接下来有N行,每行一组实数(x,y)分别描述这N个路口的坐标。接下来有若干行,每行一组整数(i,j),表示第i个路口与第j个路口间的Rome Road,以0 0结束。最后两行,每行一对实数,分别描述码头和你家的坐标。*/
1.0 0.0
2.0 1.0
1 2
0 0
0.0 0.0
2.0 2.0

### 样例输入 #1

100.0 2.0         
2                      
1.0 0.0
2.0 1.0
1 2
0 0
0.0 0.0
2.0 2.0

### 样例输出 #1
202.8284
 

解析:

dijkstra的基础练手题,这里的数据范围小,不需要堆优化,更进一步算法实现为堆优化。

在输入时要注意%d%lf的区分。

这题输出调试特别麻烦,适合拿来练习调试。

这题只要把每个结点(包括起点和终点)之间都连起来,跑一遍有实数的最短路就行了。注意下哪些地方要用实数,边权是如何算出来的(根号距离乘单位长度的花费)。

v作为用来记录的点,w记录这条边的边权,

next: 这是一个整数,通常用于表示与当前边相关的下一个边的索引。在邻接表的实现中,它用于连接具有相同起点的所有边。

int front[1010]:一个数组,用于存储每个顶点的邻接表头指针。邻接表头指针是指向链表头的指针,链表头包含了邻接表中某个顶点的所有相邻顶点的信息。通过这个指针,可以方便地访问到整个邻接表,包括其中的所有节点和相邻顶点信息。

n:顶点的数量

x[1010],y[1010]        n行,每行一组实数(x,y)分别描述这N个路口的坐标。    坐标用x[i],y[i]来表示

i,j:循环计数器

M:边的数量。

_x,_y;

先输入_x,_y;然后再使用while,从而实现以0 0结束。

用于:每行一组整数(i,j),表示第i个路口与第j个路口间为Rome Road,以0 0结束。


bool v[1010];一个布尔数组,用于标记某个顶点是否被访问过。
double dis[1010]:一个数组,用于存储从起点到每个顶点的距离。

x_1,x_2:两个实数,分别表示走Dirt Road和Rome Road一个单位长度时Caesar的不满值。

c++实现

#include<bits/stdc++.h>
using namespace std;
struct edge
{
	int v,next;
	double w;
} p[4000001];//邻接表的边
int front[1010],n,i,j,M,_x,_y;
bool v[1010];
double dis[1010],x[1010],y[1010],x_1,x_2;
void add(int u,int v,double w)
{
	p[++M].v=v;
	p[M].w=w;
	p[M].next=front[u];
	front[u]=M;
}//加边

struct point
{
	int a,b;
} q;
bool operator <(const point &x,const point &y)
{
	return x.a>y.a;
}
priority_queue<point> dui;//dijstra的堆优化
double dijstra(int st,int ed)//起点与终点
{
	q.a=0;
	q.b=st;
	dui.push(q);
	int ii;
	for (ii=1;ii<=n+2;ii++) dis[ii]=1999999999;
	dis[st]=0;
	while (!dui.empty())
	{
		int x=dui.top().b;
		dui.pop();
		if (v[x]) continue;
		v[x]=true;
		for (ii=front[x];ii;ii=p[ii].next)
		{
			if (dis[x]+p[ii].w<dis[p[ii].v])
			{
				dis[p[ii].v]=dis[x]+p[ii].w;
				if (!v[p[ii].v])
				{
					q.a=dis[p[ii].v];
					q.b=p[ii].v;
					dui.push(q);
				}
			}
		}
	}
	return dis[ed];
}
//最短路,计算两个点之间的距离,通过横纵坐标实现
double juli(double i1,double j1,double i2,double j2)
{
	return sqrt((i1-i2)*(i1-i2)+(j1-j2)*(j1-j2));
}//距离计算公式
int main()
{
	scanf("%lf%lf",&x_1,&x_2);
	scanf("%d",&n);
	for (i=1;i<=n;i++)
	 scanf("%lf%lf",&x[i],&y[i]);
	for (i=1;i<=n;i++)
	 for (j=i+1;j<=n;j++)
	 {
	 	add(i,j,x_1*juli(x[i],y[i],x[j],y[j]));
	 	add(j,i,x_1*juli(x[i],y[i],x[j],y[j]));
	 }//这里用到一个小技巧,对于romeroad,我们这里也按照dirtroad边权建边。因为romeroad边权一定更小,后面romeroad建边后,最短路最终会用短边造成影响,所以并没有什么事,就是时间费了点
	scanf("%d%d",&_x,&_y);
	while (_x!=0 && _y!=0)
	{
		add(_x,_y,x_2*juli(x[_x],y[_x],x[_y],y[_y]));
		add(_y,_x,x_2*juli(x[_x],y[_x],x[_y],y[_y]));
		scanf("%d%d",&_x,&_y);
	}
	scanf("%lf%lf%lf%lf",&x[n+1],&y[n+1],&x[n+2],&y[n+2]);
	for (i=1;i<=n;i++)
	{
		add(n+1,i,x_1*juli(x[i],y[i],x[n+1],y[n+1]));//从码头到(坐标)i
		add(i,n+1,x_1*juli(x[i],y[i],x[n+1],y[n+1]));
		add(i,n+2,x_1*juli(x[i],y[i],x[n+2],y[n+2]));//从i到家
		add(n+2,i,x_1*juli(x[i],y[i],x[n+2],y[n+2]));
	}
	add(n+1,n+2,x_1*juli(x[n+1],y[n+1],x[n+2],y[n+2]));
//以上全是读入然后add建立边,在迪杰斯特拉中进行计算最小值
	printf("%.4lf\n",dijstra(n+1,n+2));          //从码头到家的最小总不满值
	n+=2;//追求完美
	return 0;
}

拓展:

在图论中,边(u, v, w)通常表示一条从顶点u到顶点v的权重为w的路径。

u和v是边的两个端点,而w是这条边的权重。这个表示法经常用于无向图或有向图中,表示两个顶点之间的连接关系以及连接的强度或权重。在有向图中,u和v分别表示边的起点和终点,而在无向图中,它们通常表示连接的两个顶点。权重w可以用来表示各种不同的因素,比如边的长度、两个顶点之间的距离、连接的强度等。这种表示法对于分析网络结构和行为非常有用,特别是在计算机科学、工程和社交网络等领域。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值