01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况
https://vjudge.net/contest/216347#problem/B
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
已经告诉你了,这是个DP的题目,你能AC吗?
Output对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
Sample Input
1 5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5Sample Output
30
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
typedef long long ll;
using namespace std;
int main()
{
int t;
int a[105][105],dp[105][105];
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<=i;j++)
dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
}
printf("%d\n",dp[0][0]);
}
return 0;
}
题目大意:有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值
输入:n,k; n代表n个物品,k代表能挑选的最大最大重量(1<=n<=100,1<=wi,vi<=100,1<=w<=10000)
下面2*n个数分别代表每个物品的w[i],v[i]
输出:最大价值
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
typedef long long ll;
using namespace std;
int dp[150][150];
int w[150],v[150];//下面的两种写法都可以
int n,k;
/*
int solve(int a,int b)//a代表当前的物品,b代表还能装多少重量
{
if(dp[a][b]>=0)
return dp[a][b];
int rec;
if(a==n)//已经没有物品了
rec=0;
else if(b<w[a])//无法挑选这个物品
rec=solve(a+1,b);
else
rec=max(solve(a+1,b),solve(a+1,b-w[a])+v[a]);
return dp[a][b]=rec;
}
*/
int main()
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d%d",&w[i],&v[i]);
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<=k;j++)
{
if(j<w[i])
dp[i][j]=dp[i+1][j];//dp[i][j]为从第i个物品开始挑选总重小于j时,总价值的最大值
else
dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);
}
}
printf("%d\n",dp[0][k]);
return 0;
}
题目大意:
有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值
输入:n,k; n代表n个物品,k代表能挑选的最大最大重量(1<=n<=100,1<=wi<=10^7,1<=vi<=100,1<=w<=10^9)
下面2*n个数分别代表每个物品的w[i],v[i]
输出:最大价值
这题与上一题相比较,就只有限制条件不一样,但是如果用上题的方法,复杂度n*w,就显然太大了,试着思考,相比较于重量而言,价值的范围比较小,所以可以试着
改变dp的对象。之前的方法中,我们用dp针对不同的重量限制计算最大的价值。这次不妨用不同的价值针对不同的重量
定义dp[i+1][j]:=前i个物品中挑选出价值总和为j时重量最小值(不存在就是一个充分大的数值INF)。由于前0个物品什么都挑选不了,所以初始化为
dp[0][0]=0
dp[0][j]=INF
此外,前i个物品中挑选出价值总和为j时,一定有
前i-1个物品中挑选出价值总和为j的部分
前i-1个物品中挑选价值总和为j-v[i]的部分,然后再选中第i个物品
所以能够得到:dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i])
利用这一递推式,最终答案就是令dp[n][j]<=W的最大j
当然如果价值太大的话这一方法也行不通,也就会有像这种需要根据问题的规模来改变算法的情况存在
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
typedef long long ll;
using namespace std;
#define INF 1e9+7
#define MAX_N 100
#define MAX_V 100
int n,m;
int dp[MAX_N+1][MAX_N*MAX_V+1];
int w[1100],v[1100];
int main()
{
int n,m;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d",&w[i],&v[i]);
scanf("%d",&m);
fill(dp[0],dp[0]+MAX_N*MAX_V+1,INF);
dp[0][0]=0;
for(int i=0;i<n;i++)
{
for(int j=0;j<=MAX_N*MAX_V+1;j++)
{
if(j<v[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i]);
}
}
int res=0;
for(int i=0;i<=n*m;i++)
if(dp[n][i]<=m)
res=i;
printf("%d\n",res);
return 0;
}
题目:最长公共子序列
题目大意:给定两个子序列,s1s2...sn和t1t2...tn。求出这两个字符串最长的公共子序列的长度。
字符串s1s2...sn的子序列可以不表示为si1si2...sin(i1<i2<in) (就是可以不连续的意思)
限制条件:1<=n,m<=1000
样例:
输入 n=4 m=4 s="abcd" t="becd"
输出 3("bcd")
dp[i][j] i,j代表s和t的长度,dp[i][j]最长公共子序列长度
当si+1==tj+1时
dp[i+1][j+1]=max(dp[i][j]+1,dp[i][j+1],dp[i+1][j])
si+1!=tj+1时
dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1])
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
typedef long long ll;
using namespace std;
int n,m;//代表两个串的长度
char s[1100],t[1100];
int dp[1100][1100];
int main()
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
getchar();
scanf("%s%s",s,t);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(s[i]==t[j])
dp[i+1][j+1]=dp[i][j]+1;
else
dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
}
}
printf("%d\n",dp[n][m]);
return 0;
}
题目链接:https://vjudge.net/contest/216347#problem/A
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。
n=0表示数据结束。
Output对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。Sample Input
1 50 5 10 1 2 3 2 1 1 2 3 2 1 50 0Sample Output
-45 32
典型的01背包,每种菜只有买或者不买的情况,但是要注意限制条件,可以用5元买任意价格的菜,所以为了使利益最大化,我们就让5元去买最贵的,这样就行了
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
typedef long long ll;
using namespace std;
int n,m;
int dp[1100][1100];
int a[1100];
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
if(n==0)
break;
scanf("%d",&m);
if(m<5)
printf("%d\n",m);
else
{
sort(a,a+n);
for(int i=n-2;i>=0;i--)
{
for(int j=0;j<=m-5;j++)
{
if(j<a[i])
dp[i][j]=dp[i+1][j];
else
dp[i][j]=max(dp[i+1][j],dp[i+1][j-a[i]]+a[i]);
}
}
printf("%d\n",m-dp[0][m-5]-a[n-1]);
}
/* for(int i=0;i<n-1;i++)
{
for(int j=0;j<m;j++)
printf("%d ",dp[i][j]);
printf("\n");
}
*/
}
return 0;
}
题目链接:https://vjudge.net/contest/236677#problem/M
George met AbdelKader in the corridor of the CS department busy trying to fix a group of incorrect equations. Seeing how fast he is, George decided to challenge AbdelKader with a very large incorrect equation. AbdelKader happily accepted the challenge!
InputThe first line of input contains an integer N (2 ≤ N ≤ 300), the number of terms in the equation.
The second line contains N integers separated by a plus + or a minus -, each value is between 1 and 300.
Values and operators are separated by a single space.
OutputIf it is impossible to make the equation correct by replacing operators, print - 1, otherwise print the minimum number of needed changes.
Examples7
1 + 1 - 4 - 4 - 4 - 2 - 2
3
3
5 + 3 - 7
-1
题目大意:输入n,代表有n个数,数与数之家有用空格分隔的'+'或者'-',你可以把'+'改为'-',也可以把'-'改为'+',问你最少改多少次使得式子等于0
个人思路:自己没做出来,借鉴他人的··· 把这道题当作01背包来做,dp[i][j]代表从第1~i个数中使得重量为j的最少操作次数。
状态转移方程:
if(j>=a[i])
dp[i][j]=min(dp[i][j],dp[i-1][j-a[i]]) 代表不改变符号
if(j>=-a[i])
dp[i][j]=min(dp[i][j],dp[i-1][j+a[i]]+1) 代表改变符号,操作数+1
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
#define INF 0x3f3f3f
int a[310];
int dp[310][90000*2+100];//dp[i][j]表示第1~i个数,使剩余答案为j时的操作数
int main()
{
int n,sum=0;
char b;
scanf("%d",&n);
scanf("%d",&a[1]);
sum=a[1];
for(int i=2;i<=n;i++)
{
scanf(" %c %d",&b,&a[i]);
sum+=a[i];
if(b=='-')
a[i]=-a[i];//用a[i]存入每个数本身的值
}
//fill(dp,dp+10,INF);
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum*2;j++)
dp[i][j]=INF;
}
if(sum%2==1)
printf("-1\n");
else
{
dp[1][sum/2+a[1]]=0;//用sum/2来当作起点
for(int i=2;i<=n;i++)
{
for(int j=0;j<=sum*2;j++)
{
if(j>=a[i])
dp[i][j]=min(dp[i][j],dp[i-1][j-a[i]]);//不改变符号
if(j>=-a[i])
dp[i][j]=min(dp[i][j],dp[i-1][j+a[i]]+1);//改变符号
}
}
printf("%d\n",dp[n][sum/2]);
}
return 0;
}