01背包问题
采药
二维
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int t,m;
const int maxn=1005;
int v[maxn],tm[maxn],dp[maxn][maxn];
int main()
{
memset(v,0,sizeof(v));
memset(tm,0,sizeof(tm));
memset(dp,0,sizeof(dp));
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&tm[i],&v[i]);
}
for(int i=1;i<=m;i++)
for(int j=0;j<=t;j++)
{
if(j-tm[i]>=0) dp[i][j]=max(dp[i-1][j-tm[i]]+v[i],dp[i-1][j]);
else dp[i][j]=dp[i-1][j];
}
printf("%d\n",dp[m][t]);
return 0;
}
//若题目要求:恰好装满容积为V的背包的最大价值:初始化dp[0][i](i!=0)=-INF
//保证所有的状态都是从起点dp[0][0]转移得到的 每次装物品时,必定从已经装了体积为j-ci的物品的背包转移过来。
一维
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1005;
int t,m;
int v[maxn],tm[maxn],dp[maxn];
int main()
{
scanf("%d%d",&t,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&tm[i],&v[i]);
for(int i=1;i<=m;i++)
for(int j=t;j>=tm[i];j--)
{
dp[j]=max(dp[j],dp[j-tm[i]]+v[i]);
}
printf("%d\n",dp[t]);
return 0;
}
金明的预算方案
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int N,m,num;
int sfv[300][3],sfw[300][3],dp[40000+5];
int main()
{
scanf("%d%d",&N,&m);
for(int i=1;i<=m;i++)
{
int v,w;
scanf("%d%d%d",&v,&w,&num);
if(!num)
{
sfv[i][0]=v;
sfw[i][0]=w;
}
else
{
if(!sfv[num][1])
{
sfv[num][1]=v;
sfw[num][1]=w;
}
else
{
sfv[num][2]=v;
sfw[num][2]=w;
}
}
}
for(int i=1;i<=m;i++)
for(int j=N;j>=sfv[i][0];j--)
{
dp[j]=max(dp[j-sfv[i][0]]+sfv[i][0]*sfw[i][0],dp[j]);
if(j>=sfv[i][0]+sfv[i][1])
dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]]
+sfv[i][0]*sfw[i][0]
+sfv[i][1]*sfw[i][1]
,dp[j]);
if(j>=sfv[i][0]+sfv[i][2])
dp[j]=max(dp[j-sfv[i][0]-sfv[i][2]]
+sfv[i][0]*sfw[i][0]
+sfv[i][2]*sfw[i][2]
,dp[j]);
if(j>=sfv[i][0]+sfv[i][1]+sfv[i][2])
dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]-sfv[i][2]]
+sfv[i][0]*sfw[i][0]
+sfv[i][1]*sfw[i][1]
+sfv[i][2]*sfw[i][2]
,dp[j]);
}
printf("%d\n",dp[N]);
}
最长上升子序列
拦截导弹
O(n^2)
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=30000+5;
int n;
int a[maxn],dp[maxn],dp1[maxn];
int main()
{
int n=0;
while(scanf("%d",&a[++n])>=1);n--;
for(int i=1;i<=n;i++) dp[i]=dp1[i]=1;
int ans=0,ans1=0;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
{
if(a[i]<=a[j])
dp[i]=max(dp[i],dp[j]+1);
ans=max(ans,dp[i]);
if(a[i]>a[j])
dp1[i]=max(dp1[i],dp1[j]+1);
ans1=max(ans1,dp1[i]);
}
printf("%d\n%d\n",ans,ans1);
}
合唱队形
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=30000+5;
int n;
int a[maxn],dp[maxn],dp1[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) dp[i]=dp1[i]=1;
int ans1=0;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
dp[i]=max(dp[i],dp[j]+1);
}
for(int i=n;i>=1;i--)
for(int j=i+1;j<=n;j++)
{
if(a[i]>a[j])
dp1[i]=max(dp1[i],dp1[j]+1);
}
for(int k=1;k<=n;k++)
{
ans1=max(dp[k]+dp1[k]-1,ans1);
}
printf("%d\n",n-ans1);
}
方格取数问题
方格取数
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=11;
int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];
int n;
int main()
{
scanf("%d",&n);
int x,y,v;
memset(mp,0,sizeof(mp));
memset(dp,0,sizeof(dp));
while(scanf("%d%d%d",&x,&y,&v)>=3)
{
if(x==0&&y==0&&v==0) break;
else
{
mp[x][y]=v;
}
}
int sum;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
sum=i+j;
for(int i1=1,j1=sum-i1;i1<=n&&i1+1<=sum;i1++,j1=sum-i1)
{
dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1],
dp[i][j-1][i1-1][j1]),
max(dp[i-1][j][i1][j1-1],
dp[i][j-1][i1][j1-1]));
if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j];
}
}
cout<<dp[n][n][n][n]<<'\n';
return 0;
}
传纸条
蒟蒻的丑陋代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=55;
int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];
int m,n;
int main()
{
scanf("%d%d",&m,&n);
memset(mp,0,sizeof(mp));
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
int sum;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
sum=i+j;
int i1=1;
for(int j1=sum-i1;i1+1<=sum;i1++,j1=sum-i1)
{
if(i1 > m) continue;//************
if(j1 > n) continue;//************
dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1],
dp[i][j-1][i1-1][j1]),
max(dp[i-1][j][i1][j1-1],
dp[i][j-1][i1][j1-1]));
if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j];
}
}
cout<<dp[m][n][m][n]<<'\n';
return 0;
}
wzhd大佬的优化版:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[55][55][55][55],map[55][55];
int MAX(int a,int b,int c,int d)
{
return max(a,max(b,max(c,d)));
}
int main()
{
int m,n;
scanf("%d%d",&m,&n);
for(int i = 1;i <= m;i ++)
for(int j = 1;j <= n;j ++)
scanf("%d",&map[i][j]);
for(int i = 1;i <= m;i ++)
for(int j = 1;j < n;j ++)
for(int k = 1;k <= m;k ++)
for(int l = j + 1;l <= n;l ++)
dp[i][j][k][l] = MAX(dp[i - 1][j][k - 1][l],dp[i - 1][j][k][l - 1],dp[i][j - 1][k][l - 1],dp[i][j - 1][k - 1][l]) + map[i][j] + map[k][l];
printf("%d",dp[m][n - 1][m - 1][n]);
}
最长公共子序列问题
最长公共子序列
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
const int maxn=3000+5;
long a[maxn],b[maxn],dp[maxn][maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(a[i]==b[j]) dp[i][j]=max(dp[i-1][j-1]+1,
max(dp[i-1][j],dp[i][j-1]));
else dp[i][j]=max(dp[i-1][j-1],
max(dp[i-1][j],dp[i][j-1]));
}
printf("%ld\n",dp[n][n]);
return 0;
}
买帽子
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int tot=0,n,dp[505][505];
string s,ss;
char s1[505];
struct Hat
{
string a;
int l;
}hat[505];
bool cmp(Hat A,Hat B)
{
if(A.l==B.l) return A.a<B.a;
else return A.l>B.l;
}
int main()
{
scanf("%d",&n);
while(n--)
{
s=" ";s1[0]=' ';
cin>>ss;s+=ss;
int len=s.length();
for(int j=1;j<s.length();j++) s1[j]=s[len-j];//exchange
for(int i=1;i<len;i++)
dp[i][len-i]=1;
for(int i=1;i<len;i++)
for(int j=1;j<len;j++)
{
if(s[i]==s1[j])
{
dp[i][j]=max(dp[i-1][j-1]+1,
max(dp[i-1][j],dp[i][j-1]));
}
else dp[i][j]=max(dp[i-1][j-1],
max(dp[i-1][j],dp[i][j-1]));
}
hat[++tot]=(Hat){s,dp[len-1][len-1]};
}
sort(hat+1,hat+1+tot,cmp);
for(int i=1;i<=tot;i++)
{
for(int j=1;j<hat[i].a.length();j++)
cout<<hat[i].a[j];
printf("\n");
}
return 0;
}
编辑距离问题
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
string aa,bb;
int dp[4005][4005];
int main()
{
cin>>aa>>bb;
string a=" ",b=" ";
a+=aa,b+=bb;
for(int i=1;i<a.length();i++) dp[i][0]=i;//delete
for(int j=1;j<b.length();j++) dp[0][j]=j;//add
for(int i=1;i<a.length();i++)
for(int j=1;j<b.length();j++)
{
if(a[i]!=b[j])
{
dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i][j-1]+1,dp[i-1][j]+1));
}
else dp[i][j]=dp[i-1][j-1];
}
printf("%d",dp[a.length()-1][b.length()-1]);
}
区间DP
石子归并
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
const int maxn=105;
int a[maxn],dp[maxn][maxn],qzh[maxn];
int main()
{
memset(dp,0x3f3f3f3f,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i]=0;//长度为0的区间
qzh[i]=qzh[i-1]+a[i];
}
for(int i=n;i>=1;i--)
for(int j=i+1;j<=n;j++)
for(int k=i;k<j;k++)//注意不是k=i+1:保证长度为1的区间和为前缀和
{
dp[i][j]=min(dp[i][k]+dp[k+1][j]+qzh[j]-qzh[i-1],dp[i][j]);
}
printf("%d",dp[1][n]);
return 0;
}
能量项链
//考试的时候做出来的,开心QwQ
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n;
int a[400+5];
int power[400+5][400+5];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
a[2*n+1]=a[1];
for(int i=2*n;i>=1;i--)
for(int j=i+1;j<=2*n;j++)
for(int k=i;k<j;k++)
{
power[i][j]=max(power[i][j],power[i][k]+power[k+1][j]+a[i]*a[k+1]*a[j+1]);
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,power[i][i+n-1]);
printf("%d\n",ans);
return 0;
}
乘法游戏
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
int a[100+5];
int dp[105][105];
int main()
{
memset(dp,0x3f3f3f3f,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i-1][i]=0;
}
for(int i=n-2;i>=1;i--)
for(int j=i+2;j<=n;j++)
for(int k=i+1;k<j;k++)
{
dp[i][j]=min(dp[i][k]+dp[k][j]+a[i]*a[k]*a[j],dp[i][j]);
}
printf("%d\n",dp[1][n]);
return 0;
}
乘积最大
蒟蒻的5 for
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,k,dp[100][100][10],b[100];
char a[100];
int main()
{
scanf("%d%d",&n,&k);getchar();
for(int i=0;i<=n;i++)
{
scanf("%c",&a[i]);
if(i>=1)
{
dp[i][i][0]=b[i]=a[i]-'0';
}
}
int la;
for(int i=n;i>=1;i--)
{
la=10;
for(int j=i+1;j<=n;j++)
{
for(int k=i;k<j;k++)
{
dp[i][j][0]=dp[i][k][0]*la+dp[k+1][j][0];
}
}
}
for(int i=n-1;i>=1;i--)
for(int j=i+1;j<=n;j++)
for(int k=1;k<=(j-i);k++)
for(int l=i;l<j;l++)
{
for(int m=0;k-1-m>=0;m++)
dp[i][j][k]=max(dp[i][l][m]*dp[l+1][j][k-1-m],
max(dp[i][l][k-1-m]*dp[l+1][j][m],
dp[i][j][k]));
}
printf("%d\n",dp[1][n][k]);
}
wzhd(dalao)改后的4 for及总结
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,k,dp[100][100][10],b[100];
char a[100];
int main()
{
scanf("%d%d",&n,&k);getchar();
for(int i=0;i<=n;i++)
{
scanf("%c",&a[i]);
if(i>=1)
{
dp[i][i][0]=b[i]=a[i]-'0'; // 拆成个位 : 第 i 位
}
}
int la = 10;
for(int i=n;i>=1;i--) //因为我们每次向后去找他的下一位组合起来,因此倒序。
for(int j=i+1;j<=n;j++)//先组成2位数,依次增加。
dp[i][j][0]=dp[i][j-1][0]*la+dp[j][j][0];
for(int i=n-1;i>=1;i--)
for(int j=i+1;j<=n;j++)//同上枚举
for(int m=1;m<=k;m++)// times of‘*’
for(int l=i;l<=j;l++)//枚举断点
{
//枚举左右两区间各有几个“*”。
//其实只要固定左边是1其实都会枚举到的
//方便叙述 (1 * 2 * 3) * 4 /(1 * 2)* (3 * 4) 显然重了,
dp[i][j][m]=max(dp[i][l][m-1]*dp[l+1][j][0],dp[i][j][m]);
}
printf("%d\n",dp[1][n][k]);
}
对于石子归并,因为合并的顺序不同会产生不同的结果。
所以石子归并不能规定最后一个合并的对象。
而是将左边合并的结果与右边合并的结果进行合并。
所以我们合并的时候,需要右边的合并结果。
换句话说,我们需要枚举起点而使得我们可以访问到右边的区间。
而乘积最大中,由于乘法满足交换律,我们可以规定最后一个乘的对象。
假设我们规定右边不进行乘法运算,只是将左边的区间乘右边的区间组成的一个数。
而这个数是预处理好的。
由于我们只是将左边合并的结果与右边这一个数合并。
所以我们需要的条件只是从1~i的合并结果,故不需要枚举起点。
总结,合并区间的时候我们并不能盲目地去合并这个区间,而是先观察合并的顺序对合并的结果是否有影响。是否我们需要处理出每一个区间的结果。
记忆化搜索
滑雪
//开全局变量时要慎重,回溯时可能出错
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
const int maxn=1050;
int dp[maxn][maxn],mp[maxn][maxn];
int nx[]={0,0,1,-1};
int ny[]={1,-1,0,0};
int dfs(int x,int y)
{
if(dp[x][y]) return dp[x][y];
int ans=1;
for(int i=0;i<4;i++)
{
int tx=nx[i]+x,ty=ny[i]+y;
if(tx<=n&&tx>=1&&ty<=m&&ty>=1)
{
if(mp[tx][ty]<mp[x][y])
{
ans=max(ans,dfs(tx,ty)+1);
}
}
}
dp[x][y]=ans;
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&mp[i][j]);
}
int ans1=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans1=max(ans1,dfs(i,j));
cout<<ans1;
}
方案数
传球游戏
#include<cstdio>
#include<iostream>
using namespace std;
int n,m;
int dp[200][200];
int main()
{
cin>>n>>m;
dp[0][1]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(j==1) dp[i][j]=dp[i-1][n]+dp[i-1][2];
else if(j==n) dp[i][j]=dp[i-1][n-1]+dp[i-1][1];
else dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];
}
cout<<dp[m][1];
}
摆花
二维
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int a[108],f[108][108];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;i<=a[1];i++) f[1][i]=1;
for(int i=2;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=a[i];k++)
if(j-k>=0)
f[i][j]=(f[i][j]+f[i-1][j-k])%1000007;
printf("%d",f[n][m]);
}
一维
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int a[108],f[108];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
for(int k=1;k<=a[i];k++)//**从1开始的原因:自己想
if(j-k>=0)
f[j]=(f[j]+f[j-k])%1000007;
printf("%d",f[m]);
}
未完待续