动态规划 dp
1.洛谷 P1049 装箱问题
题目描述
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30,每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入输出格式
输入格式:
一个整数,表示箱子容量
一个整数,表示有n个物品
接下来n行,分别表示这n 个物品的各自体积
输出格式:
一个整数,表示箱子剩余空间。
输入输出样例
24 6 8 3 12 7 9 7
0
说明
NOIp2001普及组 第4题
思路:
f数组里记录的是最大能够存放的物体的体积,我们在输入的时候将每个物体的先在c数组中记录一下,(结合一下01背包,c数组就相当于每个物体的价值),那么同样对于每件物品我们还是有不放和放两种情况,所以转移方程就出来啦!!
f[j] = max(f[j],f[j-vi[i]]+c[i]);
(⊙v⊙)嗯~ 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 long long V,n,vi[50]; 6 long long f[500000],c[50]; 7 8 int main() { 9 cin>>V>>n; 10 for(int i=1; i<=n ;i++) { 11 cin>>vi[i]; 12 c[i]=vi[i]; 13 } 14 for(int i=1; i<=n; i++) { 15 for(int j=V; j>=vi[i]; j--) { 16 f[j] = max(f[j],f[j-vi[i]]+c[i]); 17 } 18 } 19 cout<<V-f[V]<<endl; 20 return 0; 21 }
2.P1002 过河卒
题目描述
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A点(0, 0)、B点(n, m)(n, m为不超过20的整数),同样马的位置坐标是需要给出的。
现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入输出格式
输入格式:
一行四个数据,分别表示B点坐标和马的坐标。
输出格式:
一个数据,表示所有的路径条数。
输入输出样例
6 6 3 3
6
说明
结果可能很大!
思路:
因为马所在点和马能够一步跳跃的点是不可以走的,所以要先开个数组标记一下,(大家肯定对dx,dy数组并不陌生,所以就不介绍啦~\(≧▽≦)/~),因为从0开始会出现负数下标,所以我们从1开始,那么就将数组向右下移一格。因为卒只能往右或下跳,所以对于每个点都是由它上面的点或下面的点扩展来的,(加法原理),运用类似前缀和的做法,每个点上记录的是到达此点的路径条数,所以最后一个点上记录的就是从A到B的所有路径,输出就OK。。。
(⊙v⊙)嗯~ 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const long long N = 2001; 6 long long x,y,n,m,f[N][N],vis[N][N]; 7 int dx[9] = {0,2,-2,2,-2,1,-1,1,-1}; 8 int dy[9] = {0,1,1,-1,-1,2,2,-2,-2}; 9 10 int main() { 11 cin>>n>>m>>x>>y; 12 for(int i=0; i<=8; i++) { 13 int xx=dx[i]+x,yy=dy[i]+y; 14 if(xx>=0&&xx<=m&&yy>=0&&yy<=n) { 15 vis[xx+1][yy+1]=1; 16 } 17 } 18 f[1][1]=1; 19 for(int i=1; i<=n+1; i++) { 20 for(int j=1; j<=m+1; j++) { 21 if(!vis[i][j]) { 22 f[i][j]+=f[i-1][j]+f[i][j-1]; 23 } 24 } 25 } 26 cout<<f[n+1][m+1]<<endl; 27 return 0; 28 }
3.P1004 方格取数
题目描述
设有N*N的方格图(N<=9),我们将其中的某些方格中填入正整数,而其他的方格中则放
人数字0。如下图所示(见样例):
A
0 0 0 0 0 0 0 0
0 0 13 0 0 6 0 0
0 0 0 0 7 0 0 0
0 0 0 14 0 0 0 0
0 21 0 0 0 4 0 0
0 0 15 0 0 0 0 0
0 14 0 0 0 0 0 0
0 0 0 0 0 0 0 0
. B
某人从图的左上角的A点出发,可以向下行走,也可以向右走,直到到达右下角的B
点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从A点到B点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
输入输出格式
输入格式:
输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个
表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
输出格式:
只需输出一个整数,表示2条路径上取得的最大的和。
输入输出样例
8 2 3 13 2 6 6 3 5 7 4 4 14 5 2 21 5 6 4 6 3 15 7 2 14 0 0 0
67
说明
NOIP 2000 提高组第四题
思路:
因为是两条路同时走,所以开了四维数组,i、j表示第一条路到达的点,l、k表示第二条路到达的点,走法又分为四种情况:
①两条路都从上边到达此点
②两条路都从左边到达此点
③第一条路从左边到达此点,第二条路由上边到达此点
④第一条路从上边到达此点,第二条路由左边到达此点
因为是要最大和,所以每种情况都取max,最后所得的也是最大。如果两条路同时经过了一个点,那么这个点就只加一次,(我是按都加上,然后如果同时经过再减去一个写的)。
(⊙v⊙)嗯,代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const int N = 10; 6 int n,x,y,sum[N][N],ans; 7 int num[N][N],f[N][N][N][N],vis[N][N]; 8 9 int main() { 10 scanf("%d",&n); 11 while(scanf("%d%d",&x,&y)==2) { 12 if(!x) break; 13 cin>>sum[x][y]; 14 } 15 for(int i=1; i<=n; i++) { 16 for(int j=1; j<=n; j++) { 17 for(int l=1; l<=n; l++) { 18 for(int k=1; k<=n; k++) { 19 if(i+j!=l+k) continue; 20 f[i][j][l][k] = max(f[i][j][l][k],f[i-1][j][l-1][k]); 21 f[i][j][l][k] = max(f[i][j][l][k],f[i][j-1][l][k-1]); 22 f[i][j][l][k] = max(f[i][j][l][k],f[i-1][j][l][k-1]); 23 f[i][j][l][k] = max(f[i][j][l][k],f[i][j-1][l-1][k]); 24 f[i][j][l][k]+=sum[i][j]+sum[l][k]; 25 if(i==l&&j==k) f[i][j][l][k]-=sum[l][k]; 26 } 27 } 28 } 29 } 30 cout<<f[n][n][n][n]<<endl; 31 return 0; 32 }
自己选的路,跪着也要走完!!!