2017-9-5 NOIP冲刺

5 篇文章 0 订阅
3 篇文章 0 订阅

Task 1: 售票机

题目描述:

售票机是一个类似于ATM的设备,它有一个键盘,如下图所示。

键盘分成4行8列。
你在买票的时候,输入要去的目的地即可出票。为了使得输入更快更方便,它有一个功能,在输入的过程中,它会根据你已经输入的字母去匹配可能的目的地,并推测出你要输入的下一个字母,然后键盘只显示那些字母,而其他的字母就会变成”*”。
现在给出所有的目的地和你已经输入的字母,请问现在的键盘会变成什么样?

题目分析:

因为数据范围很小,所以直接暴力就行了。

Code:

#include <cstdio>
#include <cstring>
#define rep(i,x,y) for(int i=x;i<=y;++i)

int n, len[55], L;
char ip[105];
char s[55][105], ans[35];

inline bool Issame(int p){
    rep(i,1,L)if(s[p][i] != ip[i]) return 0;
    return 1;
}

int main(){
    rep(i,1,32) ans[i] = '*';

    scanf("%d",&n);
    rep(i,1,n) scanf("%s",s[i]+1), len[i] = strlen(s[i]+1);

    scanf("%s",ip+1);
    L = strlen(ip+1);
    rep(i,1,n)if(len[i] >= L+1 && Issame(i))
        ans[s[i][L+1]-'A'+4] = s[i][L+1];

    rep(i,0,3){
        rep(j,1,8) putchar(ans[i*8+j]);
        putchar(10);
    }
    return 0;
}

Task 2: 分钱

题目描述:

两个人在街上捡到了一些钱,这些钱共有N张,他们等了很久也没有等来失主,于是决定把钱平分。但钱可能无法平分。他们先把能够平分的钱尽量先平分了,使得剩下不能平分的钱尽量少。这些不能平分的钱怎么办呢他?他们决定拿去赌场里面赌一把。他们运气太好了,那些不能平分的钱变成了双倍,于是他们就把那个钱分了。现在,请问他们每个人带回家多少钱。

题目分析:

根据题目描述,我们可以发现这其实就是一道经典的DP模型。
这里提供另外一道相同模型的题:http://new.tyvj.cn/p/1114
这里我们可以定义f[i][j]表示在前i个物品中选择,两个人拿的钱相差为j时,拿的多的那个人有多少钱。
然后的转移就类似01背包。对每个物品来说有拿(先不管是哪个人拿)和不拿两种状态。
对于不拿来说转移很简单:f[i][j] = f[i-1][j]
拿了的话我们就要分类讨论了:

  1. 是原来就拿得多的人拿了,那么他一定在哪了之后还是拿得多的哪一个,那么在拿之前两个人的差值就是j-a[i]。这种情况下,转移方程就是:f[i][j] = f[i-1][j-a[i]]+a[i]
  2. 是原来拿的少的人拿了,且拿了之后还是少的哪一个,那么在拿之前两个人的差值也是j-a[i]。这种情况下,转移方程就是:f[i][j] = f[i-1][j-a[i]]
  3. 是原来拿得少的人拿了,但拿了之后他就变成拿得多的那个人了,那么之前两个人的差值就是:a[i]-j。这种情况下,转移方程就是:f[i][j] = f[i-1][a[i]-j]+j

最后的目标状态就是f[n][0]
然后这道题的答案就是:f[n][0]+(sigma(a[i])-2*f[n][0]),化简一下就是:sigma(a[i])-f[n][0]

Code:

#include <cstdio>
#define max(a,b) (a>b ? a:b)

int n, a[505];
int f[505][100005];

int main(){
    scanf("%d",&n);
    int i, j, sum;
    for(i=1;i<=n;++i) scanf("%d",a+i);
    for(i=1,sum=a[1];i<=n;sum+=a[++i])for(j=0;j<=sum;++j){
        if(j+a[i] <= sum) f[i][j] = max(f[i-1][j], f[i-1][j+a[i]]);
        if(j >= a[i] && (f[i-1][j-a[i]]||j==a[i])) f[i][j] = max(f[i][j], f[i-1][j-a[i]]+a[i]);
        else if(f[i-1][a[i]-j]) f[i][j] = max(f[i][j], f[i-1][a[i]-j]+j);
    }
    printf("%d\n",sum-f[n][0]);
    return 0;
}

Task 3: 押韵

题目描述:

LCS表示最长公共后缀长度。如果两个单词A,B押韵,当且仅当LCS(A,B)>=MAX(A,B)-1。如果一个序列押韵,当且仅当该序列中任意相邻的两个单词押韵。现在,给你一片文章,文章中没有相同的两个单词。请你从该文章中选择任意单词,并任意排列顺序,得到一个尽量长的押韵序列。注意,每个单词只能出现一次。

题目分析:

通过数据范围可以知道,如果写暴力dp是肯定会T的。那么我们就需要对dp进行优化。
这里我们使用的是trie树,然后在树上做dp,这时对于一个字符串,它只能和它的父亲节点、兄弟节点或儿子节点所表示的字符串押韵。因为所有单词的总长度不超过3000000,所以就可以过了。
要注意的是因为所有单词的总长度不超过3000000,所以用dfs来遍历树做dp是会爆栈的,因此这里要用循环来替代dfs。

Code:

#include <map>
#include <cstdio>
#include <cstring>
using std :: map;

const int MAXN = 3000005;
#define max(a,b) (a>b ? a:b)

struct Node{
    int fa;
    bool flg;
    map <char,int> son;
}T[MAXN];
char s[MAXN];
int n, len, i, j, d;
int f[MAXN], g[MAXN];
int u, pcnt, siz[MAXN];

int main(){
    scanf("%d",&n);
    f[0] = g[0] = 1;
    for(i = 1; i <= n; ++ i){
        scanf("%s",s+1), len = strlen(s+1);
        for(j = len, u = 0; j; u = T[u].son[s[j--]])
            if(!T[u].son[s[j]]){
                T[u].son[s[j]] = ++pcnt;
                T[pcnt].fa = u, f[pcnt] = g[pcnt] = 1;
            }
        T[u].flg = 1, siz[T[u].fa] ++;
    }
    for(i = pcnt; i >= 0; -- i)if(T[i].flg){
        d = f[i]+siz[i]+T[i].flg-1;
        if(d < f[T[i].fa]) g[T[i].fa] = max(g[T[i].fa], d);
        else g[T[i].fa] = f[T[i].fa], f[T[i].fa] = d;
    }
    int ans = 0;
    for(i = pcnt; i >= 0; -- i)
        ans = max(ans, f[i]+g[i]+siz[i]+T[i].flg-2);
    printf("%d\n",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值