队内训练赛三

记录一个菜逼的成长。。

PS:来点简单dp + 贪心

A:51Nod - 1183 编辑距离

状态转移

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[i1][1]+abs(1b[i1]))
dp[i][1]=max(dp[i1][1]+abs(b[i1]b[i]),dp[i1][0]+abs(1b[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;
}

C:51Nod - 1154 回文串划分

这个状态一眼就能想到的。
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;
}

G:51nod 1412 AVL树的种类

2000个点,树高差不多15左右。
dp[i][j] := 表示节点数为i,树高为j的个数
有如下状态转移:
dp[i][j]+=dp[k][j1]dp[ik1][j1]+dp[k][j1]dp[ik1][j2]2(0k<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的水题就不写了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值