从暑假集训时入门DP的不好体验,到疯狂抵制DP,被分到DP后持续绝望,终于在欣姐的帮助下逐渐接受现实,现在先不断更新下,近期做的DP题目,立个小小的flag。从今以后每天至少两DP题。
后记:flag算是倒了,不间断更新做吧
题意:就是用所给的硬币凑出所有不超过m的数,因而数组中一定要有1的存在,没有1的话则不可能完成。
#include
using namespace std;
const int maxn = 2000010;
const int INF = 0x3f3f3f3f;
long long n, m, a[maxn];
int main()
{
scanf("%lld%lld", &n, &m);
{
for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
a[n] = m; // 把 m放入数组中,作为最大数,把数组中的数全部实现。
sort(a, a + n + 1); // 排序
if(a[0] != 1) {
printf("No answer!!!\n");
return 0;
}
long long cnt = 0, ans = 0;
for(int i = 0; i < n; i++)
{
while(cnt < a[i + 1] - 1)
{
cnt += a[i];
ans++;
if(cnt >= m) // 已经实现了m
{
printf("%lld\n", ans);
return 0;
}
}
}
printf("%lld\n", ans + 1);
}
return 0;
}
计数类DP
#include
using namespace std;
int n, k, dp[210][10];
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) dp[i][1] = 1;
for(int i = 1; i <= n; i++)
{
for(int j = 2; j <= k; j++)
{
if(i >= j) dp[i][j] = dp[i - j][j] + dp[i - 1][j - 1];
}
}
printf("%d\n", dp[n][k]);
return 0;
}
两次取物题
在这里提供两种方法:
1.四维,假设同时走,枚举每一个点最大的位置,加上两个位置的值,但当两条路走的位置相同时,减去一个值。
#include
using namespace std;
const int maxn = 15;
int n, a[maxn][maxn], x, y, d, dp[maxn][maxn][maxn][maxn];
int main()
{
scanf("%d", &n);
while(scanf("%d%d%d", &x, &y, &d) != EOF && (x || y || d))
{
a[x][y] = d;
}
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= n; k++)
{
for(int l = 1; l <= n; l++)
{
dp[i][j][k][l] = max(max(dp[i - 1][j][k - 1][l], dp[i][j - 1][k][l - 1]),
max(dp[i][j - 1][k - 1][l], dp[i - 1][j][k][l - 1])) + a[i][j] + a[k][l];
// dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
if(i == k && j == l) dp[i][j][k][l] -= a[i][j];
}
}
}
}
printf("%d\n", dp[n][n][n][n]);
return 0;
}
2.三维:优化第二个位置,注意的是只能向下和向左,所以只能有n + m 步所以只需要知道一条便即刻。
#include
using namespace std;
const int maxn = 15;
int n, a[maxn][maxn], x, y, d, dp[maxn][maxn][maxn];
int main()
{
scanf("%d", &n);
while(scanf("%d%d%d", &x, &y, &d) != EOF && (x || y || d))
{
a[x][y] = d;
}
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= n; k++)
{
int l = i + j - k;
dp[i][j][k] = max(max(dp[i - 1][j][k - 1], dp[i][j - 1][k]),
max(dp[i][j - 1][k - 1], dp[i - 1][j][k])) + a[i][j] + a[k][l];
// dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
if(i == k && j == l) dp[i][j][k] -= a[i][j];
}
}
}
printf("%d\n", dp[n][n][n]);
return 0;
}
和第三题相同类型的题。
#include
using namespace std;
const int maxn = 55;
int m, n, mp[maxn][maxn], dp[maxn][maxn][maxn][maxn];
int main()
{
while(scanf("%d%d", &m, &n) != EOF)
{
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
scanf("%d", &mp[i][j]);
}
}
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k <= m; k++)
{
int l = i + j - k;
if(l <= 0 || l > n) continue;
dp[i][j][k][l] = max(max(dp[i - 1][j][k - 1][l], dp[i][j - 1][k][l - 1]),
max(dp[i][j - 1][k - 1][l], dp[i - 1][j][k][l - 1])) + mp[i][j] + mp[k][l];
// dp[i][j][k][l] = max(dp[i][j][k - 1][l], dp[i][j][k][l - 1]) + a[k][l];
if(i == k && j == l) dp[i][j][k][l] -= mp[i][j];
}
}
}
printf("%d\n", dp[m][n][m][n]);
}
return 0;
}
三角题,然后简单的DP题。状态转移:dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];
#include
#include
#include
using namespace std;
const int maxn = 400;
int n, a[maxn][maxn], dp[maxn][maxn];
int main()
{
while(scanf("%d", &n) != EOF)
{
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
scanf("%d", &a[i][j]);
}
}
int max1 = -1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];
max1 = max(max1, dp[i][j]);
}
}
printf("%d\n", max1);
}
return 0;
}
简单递推题,找出一些数据,然后推出递推公式
if(i % 2 != 0)
dp[i] = dp[i - 1] % mod;
else
dp[i] = (dp[i - 1] + dp[i / 2]) % mod;
#include
#include
using namespace std;
const int maxn = 1000010;
const int mod = 1000000000;
long long n, dp[maxn];
void init()
{
dp[1] = 1, dp[2] = 2, dp[3] = 2, dp[4] = 4;
for(int i = 5; i < maxn - 5; i++)
{
if(i % 2 != 0)
dp[i] = dp[i - 1] % mod;
else
dp[i] = (dp[i - 1] + dp[i / 2]) % mod;
}
}
int main()
{
init();
while(scanf("%lld", &n) != EOF)
{
printf("%lld\n", dp[n] % mod);
}
return 0;
}
题意:牛吃苹果,然后他懒得运动,初始在1树下,只能移动k次,问最多拿到的苹果数。在这里我用了三维数组来记录牛的状态,其实也可以j来直接记录if(j%2 + 1 == a[i]) dp[i][j]++;,比较初始状态是确定的。
#include
#include
using namespace std;
const int maxn = 1010;
int t, w, a[maxn], dp[maxn][35][5];
int main()
{
while(scanf("%d%d", &t, &w) != EOF)
{
for(int i = 1; i <= t; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= t; ++i)
{
for(int j = 0; j <= w; j++)
{
if(j == 0)
{
dp[i][j][1] = dp[i - 1][j][1];
dp[i][j][2] = dp[i - 1][j][2];
dp[i][j][a[i]]++;
}
else
{
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][2]);
dp[i][j][2] = max(dp[i - 1][j][2], dp[i - 1][j - 1][1]);
dp[i][j][a[i]]++;
}
}
}
printf("%d\n", max(dp[t][w][1], dp[t][w][2]));
}
return 0;
}
这题就比较好了,
题意:一个小偷去偷东西,然后让我们求,在不超过最大被抓概率下,得到的最大财富。
坑点,
1.概率不是用来加的,抢完一个银行再去抢另一个银行时,概率需要成,一开始考虑的是概率*100,以他做背包的一维,但如果这样做的化最坏情况约为10^100,很遗憾多大的数组也无法满足我。
2.如果正向做的化,求被抓概率相当繁琐,不能确定哪次被抓,所以我们需要反向考虑最大不被抓的概率。
#include
#include
#include
using namespace std;
const int maxn = 110 * 110;
int t, n, m[maxn], sum;
double ht, p, h[maxn], dp[maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
sum = 0;
scanf("%lf%d", &p, &n);
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; ++i)
{
scanf("%d%lf", &m[i], &h[i]);
sum += m[i];
}
dp[0] = 1.0;
for(int i = 0; i < n; ++i)
{
for(int j = sum; j >= m[i]; --j)
{
dp[j] = max(dp[j], dp[j - m[i]] *(1 - h[i]));
}
}
for(int i = sum; i >= 0; i--)
{
if((1- dp[i]) < p)
{
printf("%d\n", i);break;
}
}
}
return 0;
}
这题还是一道比较好的题,背包。然后简单细数一下题意:就是要挑选牛去参加活动,然后需要挑选幽默和智力和最大的一群牛,还要保证任意一项均不能小于零。
明确思路,我们可以来明确一下思路,我们可以把幽默感作为价值,智力作为重量,他的最大和值作为背包容量,这就是一个简单的01背包问题,接下来需要我们做的就是对负数的处理问题,怎么处理负数呢,我们可以反向把他当作整数来处理。只要找出最后大于0的值中最大的即可。
#include
#include
#include
using namespace std;
const int maxn = 110;
const int INF = 2005 * 100;
const int S = 100 * 1000;
int n, s[maxn], f[maxn], dp[INF];
int main()
{
scanf("%d", &n);
int re = n;
for(int i = 0; i < n; i++)
{
scanf("%d%d", &s[i], &f[i]);
if(s[i] < 0 && f[i] < 0) i--, n--;
}
fill(dp, dp + INF, -INF);
dp[S] = 0;
for(int i = 0; i < n; i++)
{
if(s[i] >= 0)
{
for(int j = INF - 10; j >= s[i]; j--)
{
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
else
{
for(int j = 0; j <= INF - 10 + s[i]; j++)
{
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
}
int ans = 0;
for(int i = S; i <= INF - 10; i++)
{
if(dp[i] > 0)
ans = max(ans, dp[i] + i - S);
}
printf("%d\n", ans);
return 0;
}
01背包+上求第k大的值。
模板:
for(int i = 0; i < n; i++)
{
for(int j = V; j >= w[i]; j--)
{
for(int c = 1; c <= k; c++)
{
a[c] = dp[j][c];
b[c] = dp[j - w[i]][c] + v[i];
}
int x, y, z;
x = y = z = 1;
a[k + 1] = b[k + 1] = -1;
while(z <= k && (a[x] != - 1 || b[y] != -1))
{
if(a[x] > b[y])
{
dp[j][z] = a[x++];
}
else
{
dp[j][z] = b[y++];
}
if(dp[j][z] != dp[j][z - 1]) z++;
}
}
}
2.本题代码:
#include
#include
#include
using namespace std;
const int maxn = 1010;
int t, n, V, k, v[maxn], w[maxn], dp[maxn][35], a[maxn], b[maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d%d", &n, &V, &k);
for(int i = 0; i < n; i++) scanf("%d", &v[i]);
for(int i = 0; i < n; i++) scanf("%d", &w[i]);
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i++)
{
for(int j = V; j >= w[i]; j--)
{
for(int c = 1; c <= k; c++)
{
a[c] = dp[j][c];
b[c] = dp[j - w[i]][c] + v[i];
}
int x, y, z;
x = y = z = 1;
a[k + 1] = b[k + 1] = -1;
while(z <= k && (a[x] != - 1 || b[y] != -1))
{
if(a[x] > b[y])
{
dp[j][z] = a[x++];
}
else
{
dp[j][z] = b[y++];
}
if(dp[j][z] != dp[j][z - 1]) z++;
}
}
}
printf("%d\n", dp[V][k]);
}
return 0;
}
简单模板题,先贡献一波模板
void Zero(int w, int p)
{
for(int j = W; j >= w; j--)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Complete(int w, int p)
{
for(int j = w; j <= W; j++)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Multiple(int c, int w, int p)
{
if(c * w >= W)
{
Complete(w, p);
return;
}
int k = 1;
while(k < c)
{
Zero(k * w, k * p);
c = c - k;
k = 2 * k;
}
Zero(c * w, c * p);
}
本题题解:
#include
using namespace std;
const int maxn = 50010;
int n, c[maxn], W, w[maxn], p[maxn], dp[maxn];
void Zero(int w, int p)
{
for(int j = W; j >= w; j--)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Complete(int w, int p)
{
for(int j = w; j <= W; j++)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Multiple(int c, int w, int p)
{
if(c * w >= W)
{
Complete(w, p);
return;
}
int k = 1;
while(k < c)
{
Zero(k * w, k * p);
c = c - k;
k = 2 * k;
}
Zero(c * w, c * p);
}
int main()
{
scanf("%d%d", &n, &W);
for(int i = 0; i < n; i++) scanf("%d%d%d", &w[i], &p[i], &c[i]);
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; i++)
{
Multiple(c[i], w[i], p[i]);
}
printf("%d\n", dp[W]);
return 0;
}
#include
using namespace std;
const int maxn = 20000;
int t, n, W, dp[maxn], p[maxn], h[maxn], c[maxn];
void Zero(int w, int p)
{
for(int j = W; j >= w; j--)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Complete(int w, int p)
{
for(int j = w; j <= W; j++)
{
dp[j] = max(dp[j], dp[j - w] + p);
}
}
void Multiple(int c, int w, int p)
{
if(c * w >= W)
{
Complete(w, p);
return;
}
int k = 1;
while(k < c)
{
Zero(k * w, k * p);
c = c - k;
k = 2 * k;
}
Zero(c * w, c * p);
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &W, &n);
memset(dp, 0 ,sizeof(dp));
for(int i = 0; i < n; i++) scanf("%d%d%d", &p[i], &h[i], &c[i]);
for(int i = 0; i < n; i++)
{
Multiple(c[i], p[i], h[i]);
}
printf("%d\n", dp[W]);
}
return 0;
}
01背包记录路径.
用二维数组来记录,path[ m ] [ n ] 。其中m表示物品(m<=物品数),n表示背包状态(n<=背包容量)。
比如 path [ i ] [ j ] 表示物品 i 放在了状态 j 的背包中。 前提条件:path数组全部为0,
代码实现记录路径:
for(int i=0;i=v[i];j--)
if(f[j]
路径读取代码:
int i=n-1,j=V; //V:背包容量。n个物品
while(i>=0&&j>=0)
{
if(path[i][j])//物品i在j里
{
printf("%d ",i);//把物品i的编号输出
j-=v[i]; //读完了物品i,找下一个背包状态
}
i--;
}
int i=n-1,j=V; //V:背包容量。n个物品
while(i>=0&&j>=0)
{
if(path[i][j])//物品i在j里
{
printf("%d ",i);//把物品i的编号输出
j-=v[i]; //读完了物品i,找下一个背包状态
}
i--;
}
状压DP,先存着。
夫妻搬家,两辆车,看来回运多少次。
#include
#include
#include
#include
using namespace std;
const int maxn = 1 << 15;
const int INF = 1 << 30;
int t, N, c1, c2, w[1000], dp[maxn], h[maxn], d;
bool vis[maxn];
bool check(int x)
{
int sum = 0;
memset(vis, 0, sizeof(vis));
int tot = 0;
vis[0] = 1;
for(int i = 0; i < N; i++)
{
if(x & (1 << i))
{
sum += w[i];
for(int j = c1 - w[i]; j >= 0; j--)
{
if(vis[j]) vis[j + w[i]] = 1;
}
}
}
for(int j = c1; j >= 0; j--)
{
if(vis[j] && (sum - j) <= c2) return 1;
}
return 0;
}
int main()
{
scanf("%d", &t);
d = 0;
while(t--)
{
d++;
scanf("%d%d%d", &N, &c1, &c2);
for(int i = 0; i < N; i++) scanf("%d", &w[i]);
if(c1 > c2) swap(c1, c2);
int maxn = (1 << N) - 1;
int num = 0;
for(int i = 0; i <= maxn; i++)
if(check(i)) h[num++] = i;
for(int i = 1; i <= maxn; i++)
dp[i] = INF;
dp[0] = 0;
for(int i = 0; i < num; i++)
{
for(int j = maxn - h[i]; j >= 0; j--)
{
if(!(j & h[i])) dp[j | h[i]] = min(dp[j | h[i]], dp[j]+ 1);
}
}
printf("Scenario #%d:\n%d\n\n", d, dp[maxn]);
}
return 0;
}
拿钱去买东西,但必须钱数超过一定值才能买。
P1, Q1, P2, Q2 第一个数在前, P1 + Q2 第二个数在前 P2 + Q1保证无后效性,即第一个购买对第二个购买无影响即P1 + Q2 > P2 + Q1. 在思考一下5 6 , 5 9 先买 5 6 再买5 9 需要 14 反之 11 先选5 6,再选5 9 如果后面用到的状态前面都能给到才行。
#include
using namespace std;
const int maxn = 5010;
int n, m, dp[maxn];
struct nod
{
int P, Q, V;
};
nod a[maxn];
bool cmp(nod x, nod y)
{
return x.Q - x.P < y.Q - y.P;
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF)
{
for(int i = 0; i < n; i++) scanf("%d%d%d", &a[i].P, &a[i].Q, &a[i].V);
memset(dp, 0, sizeof(dp));
sort(a, a + n, cmp);
for(int i = 0; i < n; i++)
{
for(int j = m; j >= a[i].Q && j >= a[i].P; j--)
{
if(j >= a[i].Q)
dp[j] = max(dp[j], dp[j - a[i].P] + a[i].V);
}
}
printf("%d\n", dp[m]);
}
return 0;
}
题意:旅客去买东西,然后要我们输出买的最多东西时他的方案数,如果什么都买不了,输出。。。
分析:我们可以开二维记录方案数,和物品数,
1.体积不同方案数不发生改变时,即当前长度的方案数+1
2.同种体积得到更多方案数更新。
#include
using namespace std;
const int maxn = 510;
int t, n, m, a[maxn], dp[maxn][35];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= m; i++) dp[i][1] = 1;
for(int i = 0; i < n; i++)
{
for(int j = m; j >= a[i]; j--)
{
/*
*1.体积不同方案数不发生改变时,即当前长度的方案数+1
*2.同种体积得到更多方案数更新。
*/
if(dp[j][0] == dp[j - a[i]][0] + 1)
dp[j][1] += dp[j - a[i]][1];
else if(dp[j][0] < dp[j - a[i]][0] + 1)
dp[j][0] = dp[j - a[i]][0] + 1, dp[j][1] = dp[j - a[i]][1];
}
}
if(dp[m][0])
printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n", dp[m][1],dp[m][0]);
else
printf("Sorry, you can't buy anything.\n");
}
return 0;
}
你去买东西,然后给你不同面额钱,各有各的数量,然后让你求出最大硬币量,
看似是一个多重背包问题,实则需要我们记录硬币的数目,这样的话我们就可以转换为完全背包的问题。只需要加一个记录数目的数组即可。
#include
#include
#include
using namespace std;
const int maxn = 10100;
int P, c[5], a[5], path[10][maxn], dp[maxn];
int main()
{
a[1] = 1, a[2] = 5, a[3] = 10, a[4] = 25;
while(scanf("%d%d%d%d%d", &P, &c[1], &c[2], &c[3], &c[4]) != EOF)
{
if(P + c[1] + c[2] + c[3] +c[4] == 0) break;
for(int i = 0; i <= P; i++) dp[i] = -1;
memset(path, 0, sizeof(path));
dp[0] = 0;
for(int i = 1; i <= 4; i++)
{
for(int j = a[i]; j <= P; j++)
{
if(dp[j - a[i]] != -1 && path[i][j - a[i]] + 1 <= c[i] && dp[j] < dp[j - a[i]] + 1)
{
dp[j] = dp[j - a[i]] + 1;
path[i][j] = path[i][j - a[i]] + 1;
// cout<<"path = "<
简单的分组背包题。贡献一波模板
#include
using namespace std;
const int maxn = 110;
int m, n, a[maxn][maxn], dp[maxn];
int main()
{
while(scanf("%d%d", &n, &m) != EOF && (n + m))
{
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
scanf("%d", &a[i][j]);
for(int i = 1; i <= n; i++)
{
for(int j = m ; j >= 1; j--) // 枚举重量
{
for(int k = 1; k <= j; k++) // 枚举每一组中的重量
{
dp[j] = max(dp[j], dp[j - k] + a[i][k]);
}
}
}
cout<
题意:花园种树,然后每个位置种的树不同,要求中间的最大或最小。求最后的观赏性最大。
分析:我们可以想象一下,10 旁边可能是20 或30, 20可能是全10或全30,30可能为 10或20.
定义dp[i][j][k](1<=i<=n,1<=j<=3,1<=k<=2)
表示前i棵树所能达到的最大观赏价值和
其中,j表示哪一种树(j==0,表示树高为10;j==1,表示树高为20,等等)
k==0表示第i棵树比左右两棵树都高,否则相反;
很容易得出以下思路:
dp[i][0][0]=max(dp[i-1][1][1],dp[i-1][2][1])+a[i];
dp[i][1][0]=dp[i-1][2][1]+b[i];
dp[i][1][1]=dp[i-1][0][0]+b[i];
dp[i][2][1]=max(dp[i-1][1][0],dp[i-1][0][0])+c[i];
然后唯一需要我们注意i == n时怎么判断,我们可以直接给第一个数赋值然后枚举,再不断分别判断比他大的和比他小的。
#include
using namespace std;
const int maxn = 100010;
int n, dp[maxn][4][3];
struct nod
{
int a, b, c;
};
nod m[maxn];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d%d%d", &m[i].a, &m[i].b, &m[i].c);
int ans = -1;
for(int k = 1; k <= 3; k++)
{
for(int i = 1; i <= 3; i++)
for(int j = 0; j < 2; j++)
dp[1][i][j] = 0;
int x;
if(k == 1) x = m[1].a;
else if(k == 2) x = m[1].b;
else x = m[1].c;
dp[1][k][0] = dp[1][k][1] = x;
for(int i = 2; i <= n; i++)
{
dp[i][1][0] = max(dp[i - 1][2][1] + m[i].a, dp[i - 1][3][1] + m[i].a);
dp[i][2][0] = dp[i - 1][3][1] + m[i].b;
dp[i][2][1] = dp[i - 1][1][0] + m[i].b;
dp[i][3][1] = max(dp[i - 1][2][0] + m[i].c, dp[i - 1][1][0] + m[i].c);
}
for(int i = 1; i < k; i++)
ans = max(ans, dp[n][i][0]);
for(int i = 3; i > k; i--)
ans = max(ans, dp[n][i][1]);
}
printf("%d\n", ans);
return 0;
}
题意:这题满坑的,参加晚会,然后每个晚会的穿衣风格不同,而且脱了的衣服就不能再穿了,但可以穿多件衣服,问你需要准备几件衣服。
解法:
1.需要穿的话 dp[i][j] = dp[i][j - 1] + 1; // 穿上
2.不穿的话要脱到下一件是需要的
if(a[k] == a[j]) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]); // 没穿, 脱到k
3.边界起初,如果每次换一件需要dp[i][j] = j - i + 1; // i -> j 都换的状态
#include
#include
#include
using namespace std;
const int maxn = 110;
int T, n, a[maxn], dp[maxn][maxn];
int main()
{
scanf("%d", &T);
int tot = 0;
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
dp[i][j] = j - i + 1; // i -> j 都换的状态
for(int len = 1; len <= n; len++)
{
for(int i = 1; i + len <= n + 1; i++)
{
int j = i + len - 1;
dp[i][j] = dp[i][j - 1] + 1; // 穿上
for(int k = 1; k < j; k++)
{
if(a[k] == a[j]) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j - 1]); // 没穿, 脱到k
}
}
}
printf("Case %d: %d\n", ++tot, dp[1][n]);
}
return 0;
}