DFS是深度优先搜索算法,是一种用于图形搜索的算法。它的基本思想是:从初始状态出发,沿着最先发现的路径前进,直到最后可以到达目标状态为止
void dfs()//参数用来表示状态
{
if(到达终点状态){
...//根据题意添加
return;
} ]
//可能剪枝
if(特殊状态)//剪枝
return ;
//重复过程
for(扩展方式){
//满足什么条件才能重复继续的操作
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
//标记;
dfs();
//回溯操作
(还原标记);
//是否还原标记根据题意
}
}
}
测试链接
给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法,现在,请你按照字典序将所有的排列方法输出
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围 : 1 ≤ n ≤ 7
输入样例:
3
输出样例:
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
C/C++
#include
using namespace std;
const int N = 10;
int n;
int path[N];//保存数字组合
bool st[N];//检测该数字有没有用过
void dfs(int x){
if (x==n){
for (int i = 0; i < n; i++){
cout << path[i];
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++){
if (!st[i]){
path[x] = i;
st[i] = true;
dfs(x + 1);
st[i] = false;
}
}
}
int main(){
cin >> n;
dfs(0);
return 0;
}
JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static int n;
public static int [] vis = new int[10];
public static LinkedList path = new LinkedList<>();
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static void main(String[] args) {
n = sc.nextInt();
dfs(0);
}
private static void dfs(int x) {
if (x == n){
for (Integer item : path) {
ps.print(item + " ");
}
ps.println();
return ;
}
for (int i = 1; i <= n; i++) {
if (vis[i] == 0){
vis[i] = 1;
path.add(i);
dfs(x + 1);
vis[i] = 0;
path.removeLast();
}
}
}
}
技巧
//其实就是一个全排列,可以调用对应函数或者API
#include
#include
using namespace std;
int path[10];
int main(){
int n ;
cin >> n;
for(int i = 1 ; i <= n ; i++) path[i] = i;
do{
for(int i = 1 ; i <= n ; i++){
cout << path[i];
}
cout << endl;
}while(next_permutation(path + 1,path + n + 1));
return 0;
}
小蓝要上一个楼梯,共 15 级台阶。
小蓝每步可以上 1 级台阶,也可以上 2 级、3 级或 4 级,再多就没办法一步走到了。
每级台阶上有一个数值,可能正也可能负。每次走到一级台阶上,小蓝的得分就加上这级台阶上的数值。台阶上的数值依 次为: 1, 2, 1, 1, 1, 1, 5, 5, 4, -1, -1, -2, -3, -1, -9。
小蓝希望不超过 6 步走到台阶顶端,请问他得分的最大值是多少?
注意,小蓝开始站在地面上,地面没有分值。他最终要走到台阶顶端,所以最终一定会走到数值为 -9 的那级台阶,所以 -9
一定会加到得分里面。
答案: 5
#include
#include
using namespace std;
int score[16] = {0,1,2,1,1,1,1,5,5,4,-1,-1,-2,-3,-1,-9};
int ans;
void dfs(int x,int step,int grade) // 当前在 x 级台阶,用了step步,得了grade分
{
if(x > 15 || step > 6) return ;
if(x == 15) {
ans = max(ans,grade);
return ;
}
for(int i = 1 ; i <= 4 ; i++){
dfs(x + i,step + 1,grade + score[x + i]);
}
}
int main(){
dfs(0,0,0);
cout << ans << endl;
return 0;
}
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static int [] score = new int[]{0,1,2,1,1,1,1,5,5,4,-1,-1,-2,-3,-1,-9};
public static int ans;
public static void main(String[] args) {
dfs(0,0,0);
ps.println(ans);
}
private static void dfs(int x,int step,int grade) {
if (x == 15) {
ans = Math.max(ans,grade);
return ;
}
for (int i = 1; i <= 4; i++) {
if (x + i <= 15 && step <= 5) dfs(x + i,step + 1, grade + score[i + x]);
}
}
}
题目链接
如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)
比如,下图中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法
技巧
选不重复的方法
从12个数中任意挑选5个数,用一个哈希表维护一个选取的数的记录,每次选取的数字都存入哈希表中,如果哈希表中已经存在该数字,则重新选择。
可以先将12个数排序,然后从第一个数开始,每次挑选下一个数字即可,保证不会重复。
JAVA
错误做法
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static LinkedList path = new LinkedList<>();
public static int [] vis = new int[15];
public static int ans;
public static void main(String[] args) {
dfs(0,0);
ps.println(ans);
}
private static void dfs(int x,int num) {
if (num == 5){
if (Check()) ans++;
return ;
}
for (int i = x + 1; i <= 12; i++) {
if (vis[i] == 0){
vis[i] = 1;
path.add(i);
dfs(i,num + 1);
vis[i] = 0;
path.removeLast();
}
}
}
private static boolean Check() {
for (Integer item : path) {
boolean flag = false;
for (Integer item2 : path) {
if (item == item2) continue;
int abs = Math.abs(item - item2);
if (abs == 1 || abs == 4){ //在举例,会发现bug
flag = true;
break;
}
}
if (!flag) return false;
}
return true;
}
}
正解
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static LinkedList<Integer> path = new LinkedList<>();
public static int [] vis = new int[15];
public static int [] a = new int[] {1,2,3,4,6,7,8,9,11,12,13,14};
public static int ans;
public static void main(String[] args) {
dfs(-1,0);
ps.println(ans);
}
private static void dfs(int x,int num) {
if (num == 5){
if (Check()) ans++;
return ;
}
for (int i = x + 1; i < 12; i++) {
if (vis[i] == 0){
vis[i] = 1;
path.add(a[i]);
dfs(i,num + 1);
vis[i] = 0;
path.removeLast();
}
}
}
private static boolean Check() {
int sum = 0;
for (Integer item : path) {
int temp = 0;
for (Integer item2 : path) {
if (item == item2) continue;
int abs = Math.abs(item - item2);
if (abs == 1 || abs == 5){
temp ++ ;
}
}
if (temp == 0) return false;
sum += temp;
}
//难点判断
if (sum <= 6) return false;
return true;
}
}
#include
#include
#include
#include
#include
#include
using namespace std;
int a[12] = {1,2,3,4,6,7,8,9,11,12,13,14};
int vis[15];
int ans;
vector path;
bool Check(){
int sum = 0;
for (int i = 0; i < path.size();i++){
int temp = 0;
for (int j = 0;j < path.size();j++){
if (i == j) continue;
int cal = abs(path[i] - path[j]);
if (cal == 1 || cal == 5){
temp++;
}
}
if (temp == 0) return false;
sum += temp;
}
if (sum <= 6) return false;
return true;
}
void dfs(int x,int num){
if (num == 5){
if (Check()) ans++;
return ;
}
for(int i = x + 1; i < 12; i++){
if (vis[i] == 0){
vis[i] = 1;
path.push_back(a[i]);
dfs(i,num + 1);
vis[i] = 0;
path.pop_back();
}
}
}
int main() {
dfs(-1,0);
cout << ans << endl;
}
BFS(广度优先搜索)是一种经典的搜索算法,它常用于搜索图中的最短路径,解决最小化问题,以及在多种场景下的搜索和遍历。
特点:
广度优先搜索的思想,其中每一步都会沿着图的宽度搜索,而不是深度搜索。
一般用队列来实现。
一般用于求解最短路径问题,可以在求解过程中,每一步都能获得最短路径的值
//BFS伪代码
//使用队列queue和访问标记数组visited
queue queue;
bool visited[MAX_SIZE];
//初始化
for (int i = 0; i < MAX_SIZE; i++)
visited[i] = false;
//从源结点s出发
visited[s] = true;
queue.push(s);
//当队列非空时循环
while (!queue.empty()) {
//取出队首结点u
int u = queue.front();
queue.pop();
//最终条件
if(...条件){
//需要的结果
return ;
}
//对u的每个邻接结点进行检查
for (int v = 0; v < MAX_SIZE; v++) {
//如果v未被访问
if (!visited[v]) {
//标记v已被访问
visited[v] = true;
//将v加入
q.push(v);
}
}
}
题目链接
小明有一块空地,他将这块空地划分为 n 行 m 列的小块,每行和每列的长度都为 1,小明选了其中的一些小块空地,种上了草,其他小块仍然保持是空地。这些草长得很快,每个月,草都会向外长出一些,如果一个小块种了草,则它将向自己的上、下、左、右四小块空地扩展,这四小块空地都将变为有草的小块。
请告诉小明,k 个月后空地上哪些地方有草。
输入描述
输入的第一行包含两个整数
接下来 n 行,每行包含 m 个字母,表示初始的空地状态,字母之间没有空格。如果为小数点,表示为空地,如果字母为 g,表示种了草。
接下来包含一个整数 k。 其中,2≤n,m≤1000,1≤k≤1000。
输出描述
输出 n 行,每行包含 m 个字母,表示 k 个月后空地的状态。如果为小数点,表示为空地,如果字母为 g,表示长了草。
样例
输入
4 5 .g... ..... ..g.. ..... 2
输出
gggg. gggg. ggggg .ggg.
JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static final int [] dx = {1,-1,0,0};
public static final int [] dy = {0,0,1,-1};
public static int [][] vis = new int[1001][1001];
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
PrintStream ps = new PrintStream(System.out);
int n = sc.nextInt();
int m = sc.nextInt();
char[][] arr = new char[n][m];
for (int i = 0; i < n; i++) {
arr[i] = sc.next().toCharArray();
}
int k = sc.nextInt();
Queue q = new LinkedList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (arr[i][j] == 'g'){
q.add(new Node(i,j,0));
vis[i][j] = 1;
}
}
}
while (q.size() > 0){
Node node = q.poll();
if (node.step >= k) continue ;
for (int i = 0; i < 4; i++) {
int fx = dx[i] + node.x;
int fy = dy[i] + node.y;
if (fx < 0 || fx >= n || fy < 0 || fy >= m) continue;
if (vis[fx][fy] == 1) continue;
vis[fx][fy] = 1;
q.add(new Node(fx,fy, node.step + 1));
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (vis[i][j] == 1) ps.print("g");
else ps.print(".");
}
ps.println();
}
}
}
class Node {
int x,y,step;
public Node(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
}
C++
#include
#include
#include
#include
using namespace std;
int vis[1001][1001];
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
int n,m,k;
char arr[1001][1001];
struct Node{
int x,y,step;
Node(int x,int y,int step):x(x),y(y),step(step){};
};
int main(){
cin>>n>>m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin>>arr[i][j];
}
}
cin>>k;
queue q;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (arr[i][j] == 'g'){
q.push(Node(i,j,0));
vis[i][j] = 1;
}
}
}
while(q.size() > 0){
Node node = q.front();
q.pop();
if (node.step >= k) continue;
for (int i = 0; i < 4; i++) {
int fx = dx[i] + node.x;
int fy = dy[i] + node.y;
if (fx < 0 || fx >= n || fy < 0 || fy >= m) continue;
if (vis[fx][fy] == 1) continue;
vis[fx][fy] = 1;
q.push(Node(fx,fy,node.step + 1));
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (vis[i][j] == 1) cout<<"g";
else cout<<".";
}
cout<
题目链接
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)
只能在水平方向或垂直方向走,不能斜着
输入描述
第一行是两个整数,R和C,代表迷宫的长和宽( 1≤ R,C ≤ 40)
接下来是R行,每行C个字符,代表整个迷宫
空地格子用‘.’表示,有障碍物的格子用‘#’表示
迷宫左上角和右下角都是‘.’
输出描述
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)
计算步数要包括起点和终点,无法到达终点时输出 0
样例
输入
5 5 ..### #.... #.#.# #.#.# #.#..
输出
9
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static char [][] graph = new char[41][41];
public static int [][] vis = new int[41][41];
public static int [] dx = {1,-1,0,0};
public static int [] dy = {0,0,1,-1};
public static int n,m;
public static void main(String[] args) {
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i++) {
graph[i] = sc.next().toCharArray();
}
bfs();
}
private static void bfs() {
Queue queue = new LinkedList<>();
queue.add(new Node(0,0,1));
while (!queue.isEmpty()){
Node node = queue.poll();
if (node.x == n - 1 && node.y == m - 1){
ps.println(node.step);
return ;
}
for (int i = 0; i < 4; i++) {
int fx = dx[i] + node.x;
int fy = dy[i] + node.y;
if (fx >= n || fx < 0 || fy >= m || fy < 0) continue;
if (vis[fx][fy] == 1 || graph[fx][fy] == '#') continue;
vis[fx][fy] = 1;
queue.add(new Node(fx,fy,node.step + 1));
}
}
ps.println(0);
}
}
class Node {
int x,y,step;
public Node(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
}
C++
#include
#include
#include
#include
using namespace std;
struct Node{
int x;
int y;
int step;
};
char graph[41][41];
int vis[41][41];
int n,m;
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
void bfs(){
queue q;
Node start;
start.x = 0;
start.y = 0;
start.step = 1;
q.push(start);
while(!q.empty()){
Node node = q.front();
q.pop();
if(node.x == n - 1 && node.y == m - 1){
cout << node.step << endl;
return;
}
for(int i = 0; i < 4; i++){
int fx = node.x + dx[i];
int fy = node.y + dy[i];
if(fx < 0 || fx >= n || fy < 0 || fy >= m) continue;
if(vis[fx][fy] == 1 || graph[fx][fy] == '#') continue;
vis[fx][fy] = 1;
Node next;
next.x = fx;
next.y = fy;
next.step = node.step + 1;
q.push(next);
}
}
cout << 0 << endl;
}
int main(){
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) scanf("%s",graph[i]);
bfs();
return 0;
}
题目链接
你有一张某海域 N * N 像素的照片,.
表示海洋, #
表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中 “上下左右” 四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入格式
第一行包含一个整数 N (1 ≤ N ≤ 1000)。
以下 N 行 N 列代表一张海域照片。
照片保证第 1 行、第 1 列、第 N 行、第 N 列的像素都是海洋。
输出格式
一个整数表示答案
样例
输入
7 ....... .##.... .##.... ....##. ..####. ...###. .......
输出
1
存在一个问题,是海洋有可能将未被淹没的岛屿给分隔开,即可能增加岛屿数量
JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static char [][] graph = new char[1001][1001];
public static int [][] vis = new int[1001][1001];
public static int [] dx = {1,-1,0,0};
public static int [] dy = {0,0,1,-1};
public static int n;
public static void main(String[] args) {
n = sc.nextInt();
for (int i = 0; i < n; i++) {
graph[i] = sc.next().toCharArray();
}
int num = 0,ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] == '#' && vis[i][j] == 0){
num ++;
ans += bfs(i,j);
}
}
}
ps.println(num - ans);
}
private static int bfs(int x, int y) {
Queue queue = new LinkedList<>();
queue.add(new Node(x,y));
boolean flag = false;
while (queue.size() > 0){
Node node = queue.poll();
if (isOK(node.x,node.y)) flag = true;
for (int i = 0; i < 4; i++) {
int fx = dx[i] + node.x;
int fy = dy[i] + node.y;
if (fx < 0 || fx >= n || fy < 0 || fy >= n) continue;
if (vis[fx][fy] == 1 || graph[fx][fy] == '.') continue;
vis[fx][fy] = 1;
queue.add(new Node(fx,fy));
}
}
if (flag) return 1;
return 0;
}
private static boolean isOK(int x, int y) {
for (int i = 0; i < 4; i++) {
int fx = dx[i] + x;
int fy = dy[i] + y;
if (fx < 0 || fx >= n || fy < 0 || fy >= n) return false;
if (graph[fx][fy] == '.') return false;
}
return true;
}
}
class Node {
int x,y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
}
C++
#include
#include
using namespace std;
char graph[1001][1001];
int vis[1001][1001];
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,1,-1};
int n;
struct Node {
int x,y;
Node (int x,int y):x(x),y(y){}
};
bool isOK(int x, int y) {
for (int i = 0; i < 4; i++) {
int fx = x + dx[i];
int fy = y + dy[i];
if (fx < 0 || fx >= n || fy < 0 || fy >= n) return false;
if (graph[fx][fy] == '.') return false;
}
return true;
}
int bfs(int x, int y){
queue queue;
queue.push(Node(x,y));
bool flag = false;
while (queue.size() > 0){
Node node = queue.front();
queue.pop();
if (isOK(node.x,node.y)) flag = true;
for (int i = 0; i < 4; i++) {
int fx = node.x + dx[i];
int fy = node.y + dy[i];
if (fx < 0 || fx >= n || fy < 0 || fy >= n) continue;
if (vis[fx][fy] == 1 || graph[fx][fy] == '.') continue;
vis[fx][fy] = 1;
queue.push(Node(fx,fy));
}
}
if (flag) return 1;
return 0;
}
int main(){
scanf("%d",&n);
for (int i = 0; i < n; i++) scanf("%s",graph[i]);
int num = 0,ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] == '#' && vis[i][j] == 0){
num ++;
ans += bfs(i,j);
}
}
}
printf("%d\n",num - ans);
return 0;
}
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static int n,m;
public static int [] dx = {1,0,0,-1};
public static int [] dy = {0,-1,1,0};
public static char [] dir = {'D','L','R','U'};
public static char[][] graph;
public static int[][] vis;
public static void main(String[] args) {
n = sc.nextInt();
m = sc.nextInt();
graph = new char[n][m];
vis = new int[n][m];
for (int i = 0; i < n; i++) {
graph[i] = sc.next().toCharArray();
}
bfs(0,0);
}
private static void bfs(int x, int y) {
Queue q = new LinkedList<>();
q.add(new Node(x,y,0,""));
while (q.size() > 0) {
Node node = q.poll();
if (node.x == n - 1 && node.y == m - 1){
ps.println(node.step);
ps.println(node.path);
return ;
}
for (int i = 0; i < 4; i++) {
int fx = node.x + dx[i];
int fy = node.y + dy[i];
if (fx >= n || fx < 0 || fy >= m || fy < 0) continue;
if(vis[fx][fy] == 1 || graph[fx][fy] == '1') continue;
vis[fx][fy] = 1;
q.add(new Node(fx,fy,node.step + 1, node.path + dir[i] ));
}
}
}
}
class Node {
int x,y,step;
String path;
public Node(int x, int y, int step, String path) {
this.x = x;
this.y = y;
this.step = step;
this.path = path;
}
}
C++
#include
#include
#include
using namespace std;
const int MAXN = 1005;
int n,m;
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
char dir[4] = {'D','L','R','U'};
char graph[MAXN][MAXN];
int vis[MAXN][MAXN];
struct Node{
int x, y, step;
string path;
};
void bfs(int x, int y){
queue q;
q.push({x,y,0,""});
while(!q.empty()){
Node node = q.front();
q.pop();
if(node.x == n-1 && node.y == m-1){
cout << node.step << endl;
cout << node.path << endl;
return;
}
for(int i=0; i<4; i++){
int fx = node.x + dx[i];
int fy = node.y + dy[i];
if( fx<0 || fx>=n || fy<0 || fy>=m ){
continue;
}
if( vis[fx][fy] || graph[fx][fy]=='1' ){
continue;
}
vis[fx][fy] = 1;
q.push({fx,fy,node.step+1, node.path + dir[i]});
}
}
}
int main(){
cin >> n >> m;
for(int i=0; i> graph[i][j];
}
}
bfs(0,0);
return 0;
}
链接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JhI4Kkq-1678429113875)(C:\Users\qwe\AppData\Roaming\Typora\typora-user-images\image-20230303162415427.png)]
纯纯的dfs暴力
JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.*;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static int n,ans;
public static HashMap<String, Boolean> hp = new HashMap<>();
public static void main(String[] args) {
n = sc.nextInt();
for (int i = 2; i <= n; i++) {
LinkedList<Integer> arr = new LinkedList<>();
dfs(i,i,arr,0);
hp.clear();
arr.clear();
}
ps.println(ans + 1);
}
private static void dfs(int cnt, int res, LinkedList<Integer> arr, int sum) {
if (res == 0){
if (sum > n) return ;
arr.sort((o1, o2) -> o1 - o2);
String s = "";
for (Integer item : arr) {
s = s + item + '-';
}
if (!hp.getOrDefault(s,false)) {
// ps.println(s);
hp.put(s,true);
// ps.print(cnt + " ");
ans++;
}
return ;
}
for (int i = 1; i <= n - sum; i++) {
arr.addLast(i);
dfs(cnt,res - 1,arr,sum + i);
arr.removeLast();
}
}
}
能骗多少骗多少
正解
转移方程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ee0wNpWH-1678429113879)(C:\Users\qwe\AppData\Roaming\Typora\typora-user-images\image-20230303162248911.png)]
dp[i][j]
表示用1~i
这些数构造出j
这个数的方案数
含义为:对于每一个数i
我们都可以选k
次,要想构成j
,则由前i-1
个数构成j-k*i
。
边界为:dp[i][0] = 1
,构成0
的方案数为1
,即都不选。
AC代码
#include
using namespace std;
const int N = 110;
int dp[N][N], n, ans;
int main(){
cin>>n;
for(int i = 0;i <= n;i ++) dp[i][0] = 1;
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
for(int k = 0;k*i <= n && k*i <= j;k ++){ // 第j个i个数用k次
dp[i][j] += dp[i-1][j-k*i];
}
}
}
cout << dp[n][n] << endl;
return 0;
}
Java
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.*;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static int n;
public static void main(String[] args) {
n = sc.nextInt();
int[][] f = new int[n + 1][n + 1];
// 初始化
for (int i = 0; i <= n; i++) f[i][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 0; i * k <= n && k * i <= j ; k++) {
f[i][j] += f[i - 1][j - k * i];
}
}
}
ps.println(f[n][n]);
}
}