每日总结 12.27

浅浅对动态规划以及其中背包问题进行学习

对01背包问题进行深层次理解

动态规划

一.步骤

1.找出最优解的性质,刻画其结构特征。

2.递归定义最优解

3.自下而上计算最优值

4.根据计算得到的表构造最优解

01背包为最基础的dp

                                         01背包

题目概述

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000

输入样例

4 5

1 2

2 4

3 4

4 5

输出样例

8

注意:这里的w指物品的价值,而v指物品的体积,背包的限制条件是能装V体积的物品。而有些01背包问题描述里会用w指物品的重量,而v指物品的价值,背包的限制条件是能装W重量的物品。这两种描述里w和v的意义是不同的,需要注意!

#include <stdio.h>
int max(const int a,const int b)
{
	return a>b ? a:b;
}
int main()
{
	int w[35]={0},v[35]={0},dp[35][210]={0};
	int n,m;
	scanf("%d%d",&m,&n);
	int i,j;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&w[i],&v[i]);
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			if(j>=w[i])
			{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
			else
			{
				dp[i][j]=dp[i-1][j];
			}
		}
	}
	for(int k=0;k<=n;k++)
	{
		for(int l=0;l<=m;l++)
		{
			printf("%d ",dp[k][l]);
		}
		printf("\n");
	}
	printf("%d\n",dp[n][m]);
}

动态规划有一个浅略的了解后,同时发现记忆化搜索这种方法,与动态规划相同的地方是都是由最基本的递归简化而成,两者区别是,记忆化搜索与递归相同是自上而下进行搜索的,而动态规划是自下而上进行,与递归不同的是,两者是明显的空间换时间算法,同时了解DP后发现贪心算法是动态规划的一个特殊情况。
 

 以及对搜索深度搜索的进一步练习提升

                                     自然数拆分问题

题目描述

任何一个大于 11 的自然数 nn,总可以拆分成若干个小于 nn 的自然数之和。现在给你一个自然数 nn,要求你求出 nn 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。

输入格式

输入:待拆分的自然数 nn。

输出格式

输出:若干数的加法式子。

输入输出样例

输入 #1复制

7

输出 #1复制

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

说明/提示

数据保证,1\le n\le 81≤n≤8。

#include<stdio.h>
int a[9],n;
void sc(int k)
{
	for(int i=0;i<k;i++)
	{
		if(i==0)
		{
			printf("%d",a[i]);
		}
		else
		{
			printf("+%d",a[i]);
		}
	}
	printf("\n");
	return;
}
void dfs(int x,int y,int z)
{
	if(x==n)
	{
		return;
	}
	if(y==n)
	{
		sc(z);
		return;
	}
	for(int i=x;i<=n-y;i++)
	{
		a[z]=i;
		dfs(i,y+i,z+1);
	}
}
int main()
{
	while(~scanf("%d",&n))
	{
		dfs(1,0,0);
	}
	return 0;
}

                                              玉米迷宫

输入格式

第一行:两个用空格隔开的整数 NN 和 MM。

第 2\sim N+12∼N+1 行:第 i+1i+1 行描述了迷宫中的第 ii 行的情况(共有MM个字符,每个字符中间没有空格)。

输出格式

一个整数,表示起点到出口所需的最短时间。

题意翻译

奶牛们去一个 N\times MN×M 玉米迷宫,2 \leq N \leq 300,2 \leq M \leq3002≤N≤300,2≤M≤300。

迷宫里有一些传送装置,可以将奶牛从一点到另一点进行瞬间转移。这些装置可以双向使用。

如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置。

玉米迷宫除了唯一的一个出口都被玉米包围。

迷宫中的每个元素都由以下项目中的一项组成:

  1. 玉米,# 表示,这些格子是不可以通过的。
  2. 草地,. 表示,可以简单的通过。
  3. 传送装置,每一对大写字母 \tt{A}A 到 \tt{Z}Z 表示。
  4. 出口,= 表示。
  5. 起点, @ 表示

奶牛能在一格草地上可能存在的四个相邻的格子移动,花费 11 个单位时间。从装置的一个结点到另一个结点不花时间。

输入输出样例

输入 #1复制

5 6
###=##
#.W.##
#.####
#.@W##
######

输出 #1复制

3

说明/提示

例如以下矩阵,N=5,M=6N=5,M=6。

###=##
#.W.##
#.####
#.@W##
######

唯一的一个装置的结点用大写字母 \tt{W}W 表示。

最优方案为:先向右走到装置的结点,花费一个单位时间,再到装置的另一个结点上,花费 00 个单位时间,然后再向右走一个,再向上走一个,到达出口处,总共花费了 33 个单位时间。

#include<stdio.h>
char a[301][301];
int book[301][301];
int tx,ty,sx,sy,n,m,head,tail;
struct note
{
	int x;
	int y;
	int s;
}que[90001];
int find(int *x,int *y)
{
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i][j]==a[*x][*y]&&(i!=*x||j!=*y))
			{
				*x=i;
				*y=j;
				return 0;
			}	
		}
	}
}
void bfs(int x,int y)
{
	int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
	head=1;
	tail=1;
	book[x][y]=1;
	que[tail].x=x;
	que[tail].y=y;
	que[tail].s=0;
	tail++;
	while(head<tail)
	{
		if(a[que[head].x][que[head].y]=='=')
		{
			printf("%d",que[head].s);
			return;
		}
		if(a[que[head].x][que[head].y]>='A'&&a[que[head].x][que[head].y]<='Z')
		{
			find(&que[head].x,&que[head].y);
		}
		for(int k=0;k<=3;k++)
		{
			tx=que[head].x+next[k][0];
			ty=que[head].y+next[k][1];
			if(a[tx][ty]!='#'&&book[tx][ty]==0)
			{
				book[tx][ty]=1;
				que[tail].x=tx;
				que[tail].y=ty;
				que[tail].s=que[head].s+1;
				tail++;
			}
		}
		head++;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
	{
		scanf("%s",a[i]);
	}
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(a[i][j]=='@')
			{
				sx=i;
				sy=j;
			}
		}
	}
	bfs(sx,sy);
	getchar();
	getchar();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值