浅浅对动态规划以及其中背包问题进行学习
对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。
迷宫里有一些传送装置,可以将奶牛从一点到另一点进行瞬间转移。这些装置可以双向使用。
如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置。
玉米迷宫除了唯一的一个出口都被玉米包围。
迷宫中的每个元素都由以下项目中的一项组成:
- 玉米,
#
表示,这些格子是不可以通过的。 - 草地,
.
表示,可以简单的通过。 - 传送装置,每一对大写字母 \tt{A}A 到 \tt{Z}Z 表示。
- 出口,
=
表示。 - 起点,
@
表示
奶牛能在一格草地上可能存在的四个相邻的格子移动,花费 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;
}