01背包(物品只取一次)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
int dp[1000][1000];
typedef struct
{
int w, v;
}S;
S s[1003];
int main()
{
ios::sync_with_stdio(false);
cin >> n>>m;
for (int i = 1; i <= n; i++)
{
cin >> s[i].v >> s[i].w;
}
memset(dp, 0, sizeof(dp));
for (int j = m; j >= 0; j--)
{
if (j >= s[1].w)dp[j] = s[1].v;
else dp[j] = 0;
}
for (int i = 2; i <= n; i++)
{
for (int j = m; j >= 0; j--)
{
if (j >= s[i].w)dp[j] = max(dp[j], dp[j - s[i].w] + s[i].v);
}
}cout << dp[m] << endl;
return 0;
}
完全背包(物品可取无限次)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, v;
int dp[1003];
typedef struct
{
int v, w;
}S;
S s[1003];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> v;
for (int i = 1; i <= n; i++)
{
cin >> s[i].v >> s[i].w;
}
for (int i = 1; i <= n; i++)
{
for (int j = s[i].w; j <= v; j++)
{
dp[j] = max(dp[j], dp[j - s[i].w] + s[i].v);
}
}cout << dp[v] << endl;
return 0;
}
多重背包(每件物品可取p[i]次)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
int c[1003];
int v[1003];
int a[1003];
int dp[100005];
#define M 100000000
void zeropack(int cost, int value)
{
for (int i = m; i >= cost; i--)
{
dp[i] = max(dp[i], dp[i - cost] + value);
}
}
void completepack(int cost, int value)
{
for (int i = cost; i <= m; i++)
{
dp[i] = max(dp[i], dp[i - cost] + value);
}
}
void mutiplepack(int cost, int value, int amount)
{
int k;
if (cost * amount >= m)
{
completepack(cost, value);
return;
}
k = 1;
while (k < amount)
{
zeropack(k * cost, k * value);
amount -= k;
k <<= 1;
}
zeropack(amount * cost, amount * value);
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> n >> m && (n && m))
{
for (int i = 1; i <= n; i++)cin >> c[i];
for (int i = 1; i <= n; i++)cin >> v[i];
for (int i = 1; i <= n; i++)cin >> a[i];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)
{
mutiplepack(c[i], v[i], a[i]);
}
cout << dp[m] << endl;
}
return 0;
}
二位费用背包
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 10000
int v[N];
int w[N];
int g[N];
int dp[N];
int n,ww,gg;
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> v[i] >> w[i] >> g[i];
for (int i = 1; i <= n; i++)
{
for (int j = ww; j >= w[i]; j--)
{
for (int k = gg; k >= g[i]; k--)
{
dp[j][k] = max(dp[j][k], dp[j - w[i]][k - g[i]] + v[i]);
}
}
}cout << dp[ww][gg] << endl;
return 0;
}
分组背包
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 10000;
int v[N];
int w[N];
int dp[N];
int n, m;
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int s;
cin >> s;
for (int k = 1; k <= s; k++)
{
cin >> v[k] >> w[k];
}
for (int j = m; j >=0; i--)
{
for (int k = 1; k <= s; k++)
{
if (j >= w[k])dp[j] = max(dp[j], dp[j - w[k]] + v[k]);
}
}
}cout << dp[m] << endl;
return 0;
}
dp悬线法
P1169 [ZJOI2007]棋盘制作
[参考代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005;
int lft[N][N], rgt[N][N], upp[N][N], res[N][N];
int n, m, ans1 = -100, ans2 = -100;
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> res[i][j];
lft[i][j] = rgt[i][j] = j;
upp[i][j] = 1;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= m; j++) {
if (res[i][j] != res[i][j - 1])lft[i][j] = lft[i][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = m - 1; j >= 1; j--) {
if (res[i][j] != res[i][j + 1])rgt[i][j] = rgt[i][j + 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i > 1 && res[i][j] != res[i - 1][j]) {
upp[i][j] = upp[i - 1][j] + 1;
lft[i][j] = max(lft[i][j], lft[i - 1][j]);
rgt[i][j] = min(rgt[i][j], rgt[i - 1][j]);
}
int a = rgt[i][j] - lft[i][j] + 1;
int b = min(a, upp[i][j]);
ans1 = max(ans1, b * b);
ans2 = max(ans2, a * upp[i][j]);
}
}cout << ans1 << endl;
cout << ans2 << endl;
return 0;
}
P4147 玉蟾宫
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
const int maxn = 1003;
int res[maxn][maxn], lft[maxn][maxn], rgt[maxn][maxn];
int upp[maxn][maxn];
int ans = -1000;
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
char c;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> c;
if (c == 'F')res[i][j] = 1;
else if (c == 'R')res[i][j] = 0;
if (res[i][j])
{
lft[i][j] = rgt[i][j] = j;
upp[i][j] = 1;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 2; j <= m; j++) {
if (res[i][j] && res[i][j] == res[i][j - 1])lft[i][j] = lft[i][j - 1];
}
}
for (int i = 1; i <= n; i++) {
for (int j = m - 1; j >= 1; j--) {
if (res[i][j] && res[i][j] == res[i][j + 1])rgt[i][j] = rgt[i][j + 1];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (res[i][j])
{
if (i > 1 && res[i][j] == res[i - 1][j])
{
upp[i][j] = upp[i - 1][j] + 1;
lft[i][j] = max(lft[i][j], lft[i - 1][j]);
rgt[i][j] = min(rgt[i][j], rgt[i - 1][j]);
}
}
int a = rgt[i][j] - lft[i][j] + 1;
ans = max(ans, a * upp[i][j]);
}
}
cout << 3 * ans << endl;
return 0;
}
多天完全背包
题目大意:小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。
某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品
换回的金币数量。每天,小伟可以进行以下两种交易无限次:
1.任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
2.卖出持有的任意一个纪念品,以当日价格换回金币。每天卖出纪念品换回的金币
可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,1直
持有纪念品也是可以的。
T 天之后,小伟的超能力消失。因此他一定会在第 T 天卖出所有纪念品换回金币。
小伟现在有 M 枚金币,他想要在超能力消失后拥有尽可能多的金币。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200;
int v[maxn][maxn];
const int maxm = 10005;
int dp[maxn][maxm];
int t, n, m;
int main()
{
ios::sync_with_stdio(false);
cin >> t >> n >> m;
for (int i = 1; i <= t; i++)
{
for (int j = 1; j <= n; j++)cin >> v[i][j];
}
for (int i = 2; i <= t; i++)
{
memset(dp, 0, sizeof(dp));
for (int j = 1; j <= n; j++)
{
for (int k = 0; k <= m; k++)
{
dp[j][k] = dp[j - 1][k];
if (k >= v[i - 1][j])
dp[j][k] = max(dp[j][k], dp[j][k - v[i - 1][j]] - v[i - 1][j] + v[i][j]);
}
}
m += dp[n][m];
}
cout << m << endl;
return 0;
}
到达型01背包
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, bl, ml;
int a[55];
int dp[55][1003];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> bl >> ml;
for (int i = 1; i <= n; i++)cin >> a[i];
dp[0][bl] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = ml; j >= 0; j--)
{
if (j - a[i] >= 0)dp[i][j] = dp[i][j] || dp[i - 1][j - a[i]];
if (j + a[i] <= ml)dp[i][j] = dp[i][j] || dp[i - 1][j + a[i]];
}
}
for (int i = ml; i >= 1; i--)
{
if (dp[n][i])
{
cout << i << endl;
return 0;
}
}
cout << "-1" << endl;
return 0;
}
LIS(一个序列的最长上升子序列)
法一:n^n做法:
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<a[i])dp[i]=max(dp[i],dp[j]+1);
}
}
法二:nlogn做法:
for(int i=1;i<=n;i++)
{
cin>>a[i];
dp[i]=0x3f3f3f3f;
}
dp[1]=a[1];
int len=1;
for(int i=2;i<=n;i++)
{
int l=0,r=len,mid;
if(a[i]>dp[len])dp[++len]=a[i];
else
{
while(l<r)
{
mid=(l+r)/2;
if(dp[mid]>a[i])r=mid;
else l=mid+1;
}
dp[l]=min(a[i],dp[l]);
}
}cout<<len<<endl;
注意,事实上,nlogn做法偷了个懒,
并没有记录以每一个元素结尾的最长上升子序列的长度
LCS(两个序列的最长上升子序列)
用dp[i][j]数组来表示第一个串的前i位,第二个串的前j位的LCS的长度,
则状态转移方程:
若当前的A1[i]==A2[j],dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1)
否则:dp[i][j]=max(dp[i-1][j],dp[i][j-1])
法一:n^2做法:
int n,m;
int dp[1003][1003];
int a1[1003];
int a2[1003];
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a1[i];
for(int i=1;i<=m;i++)cin>>a2[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a1[i]==a2[j])
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
cout<<dp[n][m]<<endl;
法二:(洛谷大佬nb!)
我们知道,最朴素的dp解LIS,LCS,复杂度都为O(n^2)
我们还知道,用另一种dp解LIS,复杂度O(nlogn),
这种方法本质上是一种状态定义法,使其可以二分
LIS有O(nlogn)解法,LCS表示很嫉妒
于是,他一想:LIS求给定数列A的最长上升子序列,本质上是将
给定数列A.与一个表示大小关系的数列(此时为1到n递增排列)
的公共子序列,换言之LIS就是隐形的LCS,那么LCS一定也可以
变为LIS,只要定义出表示大小关系的数列就可以了
,例如:求
2 5 4 3 1和
1 2 4 5 3的LCS
只需要在数列1中重新定义大小关系,让2<5<4<3<1
代码如下:
int a[10003];
int b[10003];
int map[10004];
int dp[10004];
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
map[a[i]]=i; 令2<5<4<3<2
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
dp[i]=0x3f3f3f3f;
}
int len=0;
dp[0]=0;
for(int i=1;i<=n;i++)
{
int l=0,r=len,mid;
if(map[b[i]]>dp[len])dp[++len]=map[b[i]];
else
{
while(l<r)
{
mid=(l+r)/2;
if(dp[mid]>map[b[i]])r=mid;
else l=mid+1;
}dp[l]=min(dp[l],map[b[i]]);
}
}
cout<<len<<endl;
LICS
# include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[3010][3010];
int a[3010];
int b[3010];
int n;
int main()
{
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i <= n; i++)cin >> b[i];
for (int i = 1; i <= n; i++)
{
int maxx = 1;
for (int j = 1; j <= n; j++)
{
dp[i][j] = dp[i - 1][j];
if (b[j] < a[i])maxx = max(maxx, dp[i - 1][j] + 1);
if (a[i] == b[j])dp[i][j] = max(dp[i][j], maxx);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans = max(ans, dp[n][i]);
}
cout << ans << endl;
return 0;
}