记录一个菜逼的成长。。
PS:来点简单dp + 贪心
状态转移
int cost = (s1[i-1] == s2[j-1] ? 0 : 1);
int delection = dp[i][j-1] + 1;
int insertion = dp[i-1][j] + 1;
int substitution = dp[i-1][j-1] + cost;
dp[i][j] = min(delection,min(insertion,substitution));
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int maxn = 1000 + 10;
char s1[maxn],s2[maxn];
int dp[maxn][maxn];
int editDistance(char s1[],char s2[])
{
cl(dp,0);
int len1 = strlen(s1),len2 = strlen(s2);
for( int i = 1; i <= len1; i++)dp[i][0] = i;
for( int i = 1; i <= len2; i++ )dp[0][i] = i;
for( int i = 1; i <= len1; i++ ){
for( int j = 1; j <= len2; j++ ){
int cost = (s1[i-1] == s2[j-1] ? 0 : 1);
int delection = dp[i][j-1] + 1;
int insertion = dp[i-1][j] + 1;
int substitution = dp[i-1][j-1] + cost;
dp[i][j] = min(delection,min(insertion,substitution));
}
}
return dp[len1][len2];
}
int main()
{
while(~scanf("%s%s",s1,s2)){
printf("%d\n",editDistance(s1,s2));
}
return 0;
}
B:51Nod - 1270 数组的最大代价
想要使差值的绝对值最大,不是取1就是取b[i] (两个峰值)
dp[i][0] := 表示第i个取1的最大值
dp[i][1] := 表示第i个取b[i]的最大值
有如下状态转移
dp[i][0]=max(dp[i][0],dp[i−1][1]+abs(1−b[i−1]))
dp[i][1]=max(dp[i−1][1]+abs(b[i−1]−b[i]),dp[i−1][0]+abs(1−b[i]))
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define fin freopen("D://in.txt","r",stdin)
#define fout freopen("D://out.txt","w",stdout)
const int maxn = 50000 + 10;
int dp[maxn][2],b[maxn];
int main()
{
//fin,fout;
int n;
while(~scanf("%d",&n)){
for( int i = 1; i <= n; i++ )scanf("%d",b+i);
cl(dp,0);
for( int i = 2; i <= n; i++ ){
dp[i][1] = max(dp[i-1][1] + abs(b[i-1]-b[i]) ,dp[i-1][0] + abs(1-b[i]));
dp[i][0] = max(dp[i][0],dp[i-1][1] + abs(1-b[i-1]));
}
printf("%d\n",max(dp[n][1],dp[n][0]));
}
return 0;
}
这个状态一眼就能想到的。
dp[i] := 表示以第i个字符结尾的回文串的数量
有如下状态转移:
dp[i]+=dp[j](substring(j+1,i)是个回文串)
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
const int maxn = 5000 + 10;
char str[maxn];
int dp[maxn];
bool check(int l,int r)
{
while(l <= r){
if(str[l] != str[r])return false;
l++,r--;
}
return true;
}
int main()
{
while(~scanf("%s",str)){
cl(dp,127);dp[0] = 0;
for( int i = 0; str[i]; i++ ){
for( int j = i; j >= 0; j-- ){
if(check(j,i)){
dp[i+1] = min(dp[j]+1,dp[i+1]);
}
}
}
printf("%d\n",dp[strlen(str)]);
}
return 0;
}
D:SPOJ ABA12C - Buying Apples!
题目大意:
有n个人,你要买k千克的苹果,只能一袋一袋的买
给出k个整数,第i个整数表示i千克的苹果一袋
ai
元,负数表示不存在i千克的苹果
其实这题n是没用的。。
由于没说苹果可以买的数量,所以是个裸的完全背包的问题
所以就很简单了
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
int dp[maxn],a[maxn];
int main()
{
int T;scanf("%d",&T);
while(T--){
int n,k;
scanf("%d%d",&n,&k);
for( int i = 1; i <= k; i++ ){
scanf("%d",a+i);
}
cl(dp,INF);
dp[0] = 0;
for( int i = 1; i <= k; i++ ){
if(a[i] != -1){
for( int j = i; j <= k; j++ ){
dp[j] = min(dp[j],dp[j-i] + a[i]);
}
}
}
printf("%d\n",dp[k] != INF ? dp[k] : -1);
}
return 0;
}
E:SPOJ ACODE - Alphacode
题目大意:
A-Z表示1-26的数字
现在给你一个字符数字序列
问有几种理解方案。
这题的思路跟C题差不多。
dp[i] := 表示以第i个数字结尾的方案数。
有如下状态转移:
dp[j]+=dp[i](substring(i+1,j)的数≤26)
这里要注意第i+1个数字不能是0,不然就有前导0了,是不符合条件的
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 5000 + 10;
LL dp[maxn];
char str[maxn];
int main()
{
while(~scanf("%s",str) && str[0] != '0'){
cl(dp,0);
dp[0] = 1;
for( int i = 0; str[i]; i++ ){
if(str[i] != '0'){
int x = 0;
for( int j = i; str[j]; j++ ){
x = x * 10 + str[j] - '0';
if(x > 26)break;
dp[j+1] += dp[i];
}
}
}
printf("%lld\n",dp[strlen(str)]);
}
return 0;
}
F:SPOJ AIBOHP - Aibohphobia
题目大意:
给你一个字符串,如果要使它变为回文串,最少需要加多少个字符。
只要将原串翻转,与原串求LCS。其实就是求原串里的最大回文串长度是多少。
再把原串的长度减去LCS就是答案了。
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 6100 + 10;
int dp[maxn][maxn];
char s1[maxn],s2[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s1);
strcpy(s2,s1);
int len = strlen(s1);
reverse(s2,s2+len);
cl(dp,0);
for( int i = 0; s1[i]; i++ ){
for( int j = 0; s2[j]; j++ ){
if(s1[i] == s2[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",len - dp[len][len]);
}
return 0;
}
2000个点,树高差不多15左右。
dp[i][j] := 表示节点数为i,树高为j的个数
有如下状态转移:
dp[i][j]+=dp[k][j−1]∗dp[i−k−1][j−1]+dp[k][j−1]∗dp[i−k−1][j−2]∗2(0≤k<i)
当前状态都可以由左右子树得到。
所以假设枚举左子树的节点个数k,右子树的节点个数就是i-k-1
由于是AVL树,左右子树高度可以相等也可以相差1
相差1时有两种情况要乘2
然后注意模的运算
时间复杂度
O(n2logn)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2000 + 10;
const int MOD = 1e9 + 7;
LL dp[maxn][16];
void init()
{
dp[0][0] = 1;
dp[1][1] = 1;
for( int i = 2; i <= 2000; i++ ){
for( int j = 2; j < 16; j++ ){
for( int k = 0; k < i; k++ ){
dp[i][j] = dp[i][j] + dp[k][j-1] * dp[i-k-1][j-1] % MOD + dp[k][j-2] * dp[i-k-1][j-1] % MOD * 2 % MOD;
dp[i][j] %= MOD;
}
}
}
}
int main()
{
init();
int n;
while(~scanf("%d",&n)){
LL ans = 0;
for( int i = 1; i < 16; i++ )ans = (ans + dp[n][i]) % MOD;
printf("%lld\n",ans);
}
return 0;
}
H:SPOJ ANARC05B - The Double HeLiX
题目大意:
一个n,然后n个数
一个m,然后m个数
你可以从任意一个序列开始走,每走到一个两个序列共有的数,你可以选择跳往另一个序列继续走(只能在共有的数跳往另一个序列),问怎样走使得最后的结果最大
这题是个贪心的题
先标记a序列的所有值的位置,再去遍历b序列,记录前缀和,并且如果在b序列中发现有值在a中,记录这个数上一个共有的数的位置。
最后再去遍历a序列,记录每两个相邻共有数区间的和,与b序列比较,取最大值
注意两个序列可能一个共有的数都没有
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int maxn = 100000 + 10;
int a[maxn],b[maxn];
int posa[maxn],posb[maxn],sumb[maxn],last[maxn];
int main()
{
int n,m;
while(~scanf("%d",&n),n){
cl(posa,0),cl(posb,0),cl(sumb,0);
cl(last,0);
int pre = INF;
for( int i = 1; i <= n; i++ ){
scanf("%d",a+i);
posa[a[i] + 10000] = i;
}
int ans = 0,sum = 0;
scanf("%d",&m);
for( int i = 1; i <= m; i++ ){
scanf("%d",b+i);
sumb[i] = sumb[i-1] + b[i];
if(posa[b[i]+10000]){
if(pre == INF)last[b[i]+10000] = 0;
else last[b[i]+10000] = posb[pre];
pre = b[i] + 10000;
posb[pre] = i;
}
}
for( int i = 1; i <= n; i++ ){
sum += a[i];
if(posa[a[i]+10000] && posb[a[i]+10000]){
ans += max(sum,sumb[posb[a[i]+10000]] - sumb[last[a[i]+10000]]);
sum = 0;
}
}
if(pre == INF)pre = 0;
ans += max(sum,sumb[m] - sumb[posb[pre]]);
printf("%d\n",ans);
}
return 0;
}
I:51Nod - 1163 最高的奖励
贪心。
见这里
J:SPOJ ANARC09A - Seinfeld
题目大意:
给你一个大括号序列
你可以用左括号替换右括号,用右括号替换左括号
问最少需要替换几次使得括号序列是匹配的。
可以先用栈去掉已经匹配的括号序列
然后分别求出剩下的数量的左括号数量和右括号数量
然后ans = (cnt1+1)/2 + (cnt2+1)/2;
别问我为什么。。
我列了前几个,胡乱猜了一下。就过了。
#include <bits/stdc++.h>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int maxn = 2000 + 10;
char str[maxn];
int main()
{
int cas = 1;
while(~scanf("%s",str) && str[0] != '-'){
stack<char>st;
for( int i = 0; str[i]; i++ ){
if(str[i] == '}' && !st.empty() && st.top() == '{'){
st.pop();
}
else st.push(str[i]);
}
int ans = 0,cnt1 = 0,cnt2 = 0;
while(!st.empty()){
if(st.top() == '{')cnt1++;
else cnt2++;
st.pop();
}
ans += (cnt1+1)/2 + (cnt2+1)/2;
printf("%d. %d\n",cas++,ans);
}
return 0;
}
K:SPOJ ADAQUEUE - Ada and Queue
题目大意:
有q个操作。
有5种操作
back :输出队尾元素,并弹出
front :输出队首元素,并弹出
reverse :翻转队列
push_back N :将N放入队尾
toFront N :将N放入队首
可以用STL的deque来维护
设置个flag表示哪里是队首队尾,操作根据flag来
reverse就修改flag。
所以就很简单了。
#include <vector>
#include <cstdio>
#include <deque>
#include <queue>
using namespace std;
#define pb push_back
char ope[20];
int main()
{
int q;
while(~scanf("%d",&q)){
deque<int>dq;
int flag = 0,x;
for( int i = 0; i < q; i++ ){
scanf("%s",ope);
if(ope[0] == 'b'){
if(dq.empty()){
puts("No job for Ada?");
continue;
}
if(!flag){
printf("%d\n",dq.back());dq.pop_back();
}
else {
printf("%d\n",dq.front());dq.pop_front();
}
}
if(ope[0] == 'f'){
if(dq.empty()){
puts("No job for Ada?");
continue;
}
if(!flag){
printf("%d\n",dq.front());dq.pop_front();
}
else {
printf("%d\n",dq.back());dq.pop_back();
}
}
if(ope[0] == 'r'){
flag ^= 1;
}
if(ope[0] == 'p'){
scanf("%d",&x);
if(!flag)dq.push_back(x);
else dq.push_front(x);
}
if(ope[0] == 't'){
scanf("%d",&x);
if(!flag)dq.push_front(x);
else dq.push_back(x);
}
}
}
return 0;
}
后面四题cf div2的水题就不写了。