写在前面的话:我也是初学,有些分析或知识会有错误,望各位大佬们指教
目录
-
-
- 1:POJ 3280 Cheapest Palindrome (动态规划)
- 2:POJ 3046 Ant Counting (动态规划)
- 3: POJ 3614 Sunscreen (贪心 + 优先队列)
- 4:CodeForces - 1339D Edge Weight Assignment(构造 + 思维)
- 5:POJ 3666 Making the Grade(思维 + 动态规划)
- 6:POJ 2991 Crane(线段树)
- 7: POJ 3468 A Simple Problem with Integers(树状数组)
- 8:POJ 2184 Cow Exhibition(变种01背包)
- 9:CodeForces - 1337C Linova and Kingdom (贪心 + 图论)
- 10: POJ 2010 Moo University - Financial Aid (优先队列)
-
1:POJ 3280 Cheapest Palindrome (动态规划)
- 题意:
给一个字符串,各个字母添加和删除所需花费的数额,问将字符串修改成回文,最低花费为多少。 - 分析:
对于字符串的操作一直不太熟悉,本题的动态规划分析也不太简单。如何用dp把状态记录?其实与区间有关系,用 dp [ i ] [ j ] 表示 i 到 j 这一段字符串修改的最低费用。分析不同情况。
- s [ i ] 与 s [ j ] 相同,那么就不用修改了,直接等于 s [ i + 1 ] [ j - 1 ]。
- s [ i ] 与 s [ j ] 不相同,那么分别有两个子串进行讨论,s [ i + 1 ] [ j ] 和 s [ i ] [ j - 1 ],对于前项,我们需要讨论的是在前面删除 s [ i ] ,还是在末尾添加 s [ i ]。对于后项的讨论一样。
- 代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<set>
#include<map>
#include<climits>
#include<cstdio>
#include<string>
#pragma warning(disable : 4996)
using namespace std;
int dp[2010][2010], add[30], del[30];
char s[2010];
int main() {
int i, j, n, m, x, y, k;
char ch;
cin >> n >> m;
cin >> s;
for (i = 0; i < n; i++)
{
cin >> ch >> x >> y;
add[ch - 'a'] = x;
del[ch - 'a'] = y;
}
for (i = m - 1; i >= 0; i--)
{
dp[i][i] = 0;
for (j = i + 1; j < m; j++)
{
dp[i][j] = INT_MAX;
if (s[i] == s[j])
dp[i][j] = dp[i + 1][j - 1];
else
{
dp[i][j] = min(dp[i][j], min(dp[i + 1][j] + add[s[i] - 'a'], dp[i + 1][j] + del[s[i] - 'a']));
dp[i][j] = min(dp[i][j], min(dp[i][j - 1] + add[s[j] - 'a'], dp[i][j - 1] + del[s[j] - 'a']));
}
}
}
cout << dp[0][m - 1] << endl;
system("pause");
return 0;
}
2:POJ 3046 Ant Counting (动态规划)
- 题意:
给 n 种蚂蚁,每种蚂蚁有不同数量,问从s到e总共的组合数有多少。 - 分析:
经典的多重集组合数问题,关键是如何推出递推式。首先要设定dp数组代表什么,本题有n种物品,需要取k个,所以定义 dp [ i ] [ j ] 为取前 i 种中取 j 个的取法。那么假设第 i 种取 k 个,即前 i - 1 种取 j - k 种。那么有如下:
for(int k = 0; k <= min(j, b[i]); k++)
dp[i][j] += dp[i-1][j-k];
因为涉及三重循环,这样的复杂度可能会比较大,因此有如下优化:
for(int k = 0; k <= min(j - 1, b[i]); k++)
dp[i][j] += dp[i-1][j-k];
dp[i][j] = dp[i][j] - dp[i-1][j-k-1] + dp[i-1][j];
相当于:
dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-1-b[i-1]];
- 代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cmath>
#include<string>
#include <cstdio>
#include<stack>
#include<cstring>
#include<utility>
#include<climits>
#pragma warning(disable : 4996)
using namespace std;
int b[1010];
int dp[1005][100005];
const int MOD = 1000000;
int main() {
int n, m, s, e, i, j, k;
cin >> n >> m >> s >> e;
for (i = 0; i < m; i++)
{
cin >> k;
b[k - 1]++;
}
for (i = 0; i <= n; i++)
dp[i][0] = 1;
for (i = 1; i <= n; i++)
for (j = 1; j <= e; j++)
{
if (j - b[i - 1] - 1 >= 0)
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - b[i - 1] - 1] + MOD) % MOD;
else
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % MOD;
}
int sum = 0;
for (i = s; i <= e; i++)
{
sum += dp[n][i];
sum %= MOD;
}
cout << sum << endl;
system("pause");
return 0;
}
3: POJ 3614 Sunscreen (贪心 + 优先队列)
- 题意:
有n头牛,他们需要的防晒霜度数区间分别为 xi-yi ,有m瓶防晒霜,分别有不同的数量和度数,问最多能让多少头牛涂上防晒霜。 - 分析:
先将防晒霜按度数从小到大排序,然后尽可能把度数小的留给需求区间相对应小的牛。关键是如何处理牛的排序,是按 xi 排序,还是 yi 排序。运用贪心的思想,先按 xi 从小到大排序,然后选择相对应的防晒霜,取所有 xi 低于该度数的牛,然后再从中取这群牛中 yi 最小的,这里用优先队列进行操作,达到贪心的目的。 - 代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>