NOIP模拟题 2016.8.27 [贪心] [DP] [计数问题]

原创 2016年08月28日 18:52:33

LGTB 与偶数
LGTB 有一个长度为N 的序列。当序列中存在相邻的两个数的和为偶数的话,LGTB 就能把它们删掉。
LGTB 想让序列尽量短,请问能将序列缩短到几个数?
输入
第一行包含一个数N 代表序列初始长度
接下来一行包含N 个数a1, a2, …, aN,代表序列
对于50% 的数据,1  N  1000
对于100% 的数据,1  N  105, 0  ai  109
输出
输出包含一个数代表操作后序列最短长度
样例
样例输入样例输出
10
1 3 3 4 2 4 1 3 7 1
2
2


LGTB 与序列
LGTB 有一个长度为N 的序列A,现在他想构造一个新的长度为N 的序列B,使得B 中的任意两个数都
互质。
并且他要使ai与bi对应项之差最小
请输出最小值
输入
第一行包含一个数N 代表序列初始长度
接下来一行包含N 个数A1, A2, …, AN,代表序列A
对于40% 的数据,1  N  10
对于100% 的数据,1  N  100, 1  ai  30
输出
输出包含一行,代表最小值
样例
样例输入样例输出
51
6 4 2 8
3
样例说明
样例中B 数组可以为1 5 3 1 8
3


LGTB 与大数
LGTB 有一个非常大的数,并且他想对它进行Q 次操作
每次操作是把这个大数中的某种数字全部替换成一个数字串
他想知道Q 次操作之后得到的数对1000000007(109 + 7) 取模的结果,请输出给他
输入
输入第一行代表一个串S 代表初始的数
接下来一行有一个数字Q 代表操作次数
接下来Q 行,每行一个形如a->b1b2b3…bk 的串,代表将a 变成b1b2b3…bk
对于40% 的数据,1  jSj  1000,1  Q  10
对于100% 的数据,1  jSj  105,1  Q  105,询问中b 串的总长度不超过105
注意b 串可以为空
输出
输出包含一个数字,代表LGTB 想要的结果
样例
样例输入样例输出
123123
1
2->00
10031003
样例输入样例输出
222
2
2->0
0->7
777
4


T1:
容易发现对于可合并项,不同的合并顺序不会对答案有影响,那么可以用栈来维护,边读边处理(考试的时候没有想到用栈。。就手写了个链表来维护)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 100005;
struct Node
{
    int pre,next;
    int val;
}node[maxn];
#define pre(i) node[i].pre
#define next(i) node[i].next
#define val(i) node[i].val
int n;
int a[maxn];
inline void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    for(int i=1;i<=n;i++)
    {
        val(i)=a[i]&1;
        pre(i)=i-1;
        next(i)=i+1<=n?i+1:0;
    }
    next(0)=1;
}
int work()
{
    int last=0,now=1;
    while(now)
    {
        if(!last) last=now,now=next(now);
        if(!now) break;
        if(val(last)==val(now))
        {
            last=pre(last);
            now=next(now);
            next(last)=now;
            pre(now)=last;
        }
        else last=now,now=next(now);
    }
    int ret=0;
    int root=next(0);
    while(root)
    {
        ret++;
        root=next(root);
    }
    return ret;
}
int main()
{
    freopen("even.in","r",stdin);
    freopen("even.out","w",stdout);
    init();
    int ans=work();
    printf("%d",ans);
    return 0;
}

T2:
容易想到状压dp,但是却不容易想到优化。。。
首先如果原数列中Ai>Aj,那么最优答案一定是Bi>Bj的情况。
这样的话,由于原数不大于30,那么B数列中的数一定不超过58,这样那么如果让前面的大数先用大的Bi去匹配,然后剩下的用1去填,一定可以得到最优解。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 105;
const int maxp = 17;
const int prime[] = {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; // 16 primes
int n,a[maxn];
int dp[maxp][1<<maxp];
int status[maxn];
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    sort(a+1,a+n+1,greater<int>());
    memset(status,0,sizeof(status));
    for(int i=1;i<=58;i++)
        for(int j=1;j<=16;j++)
            if(i<prime[j]) break;
            else if(i%prime[j]==0) status[i]|=(1<<j-1);
}
int dynamic()
{
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=min(n,16);i++) // current to fill in
        for(int S=0;S<(1<<16);S++) // last status
            for(int num=1;num<=58;num++)
                if(!(status[num]&S))
                    smin(dp[i][S|status[num]],dp[i-1][S]+abs(a[i]-num));
    int ans=INF;
    for(int S=0;S<(1<<16);S++)
        smin(ans,dp[min(n,16)][S]);
    return ans;
}
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    init();
    int ans=dynamic();
    if(n>16) for(int i=17;i<=n;i++) ans+=abs(a[i]-1);
    printf("%d",ans);
    return 0;
}

T3:
这题竟然用DP!!!!!!!完全没看出来。。
要抓住每一次修改对相同字符所做的操作,在以后的任何询问时都是一样的序列,这样就可以用dp来实现记忆化,从而达到省时的目的。
那么可以构成类似于一棵树的样子,然后从底向上计算。
考虑子树合并。。
每一条串从后往前进行处理,一边处理数值一边维护数字长度,方便下一次计算。
除此之外,数的长度也要取模!!这样就可以用费马小定理了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int maxn = 100005;
const int mod = 1000000007;
int quick_exp(int a,int p)
{
    if(!p) return 1;
    if(p==1) return a%mod;
    int tmp=quick_exp(a,p>>1);
    tmp=(LL)tmp*tmp%mod;
    if(p&1) return (LL)tmp*a%mod;
    else return tmp;
}
string s;
struct Query
{
    int val;
    string ss;
}que[maxn];
int q;
void init()
{
    cin>>s;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        que[i].val=ch-'0';
        getchar();
        cin>>que[i].ss;
        que[i].ss.erase(0,1);
    }
}
#define maxbit 15
#define SIZE 9
int dp[maxn][maxbit],len[maxn][maxbit];
int dynamic()
{
    memset(len,0,sizeof(len));
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=SIZE;i++) dp[q+1][i]=i,len[q+1][i]=1;
    for(int i=q;i>=1;i--)
        for(int j=0;j<=SIZE;j++)
            if(que[i].val^j)
            {
                dp[i][j]=dp[i+1][j];
                len[i][j]=len[i+1][j];
            }
            else
            {
                dp[i][j]=0; len[i][j]=0;
                int lens=que[i].ss.size();
                for(int p=lens-1;p>=0;p--)
                {
                    int to=que[i].ss[p]-'0';
                    dp[i][j]+=(LL)dp[i+1][to]*quick_exp(10,len[i][j])%mod;
                    if(dp[i][j]>=mod) dp[i][j]-=mod;
                    len[i][j]+=len[i+1][to]%(mod-1);
                    if(len[i][j]>=mod-1) len[i][j]-=mod-1; // Fermat Theory
                }
            }
    int lens=s.size();
    int d=0,l=0;
    for(int p=lens-1;p>=0;p--)
    {
        int to=s[p]-'0';
        d+=(LL)dp[1][to]*quick_exp(10,l)%mod;
        if(d>=mod) d-=mod;
        l+=len[1][to]%(mod-1);
        if(l>=mod-1) l-=mod-1;
    }
    return d;
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    init();
    int ans = dynamic();
    printf("%d",ans);
    return 0;
}
版权声明:S'il vous plait.

【NOIP模拟题】【贪心】【动态规划DP】2016.11.12第二题题解

B 这道题有两种解法,贪心和DP。 1.贪心 我们先将正数合并,负数记录,当取不下时交换最小的负数#include #include #include #include #include #in...

NOIP模拟题[贪心][DP][数论]

改程序之前,写程序之前,确保自己理解了,不然效率会很低。 写程序少用复制黏贴,容易细节出错,不好调试。 T1: 题意: 给定两字符串,判断B串是否是A串的字串且输出B串每个字母的匹配位置字典序...

NOIP模拟题 River Path Word[排序][贪心][DP]

一.Word 题意:给定n个长度小于100的,全为大写字母的单词,求一共有多少种,判断是一种的标准是:两个单词中每个字母的个数都相同;n...

NOIP模拟题 2016.11.18 [数论] [计数] [并查集]

第一题:信(believe.cpp/c/pas) 背景描述: 一切死亡都有冗长的回声 —— 《一切》北岛 给定一个N个元素的序列A, 定义Bi = (Ai and A1) + (Ai and...

【NOIP 模拟题】[T2]分解数(线性筛+贪心)

寒雾浓烟里,凋零了落花

NOIP模拟题 [暴力][贪心][栈][dfs][找规律]

不擅长写暴力,要多练。 学习一下传说中的打表找规律。 定数组大小之前一定要认真地算并且留够变化区间(即如果算出来是1e5左右,你开1e5,你死了。(我就是这么死的摔,长记性啊。) 稳啊稳啊,每次...

NOIP模拟题[贪心][离散化][LIS]

思考深入再深入! 小心MLE(躺 T1: 题意: 有n个怪物,生命值分别是hi,你有M魔法值,魔法值可用来使某一怪物失去2点生命值或所有怪物失去1生命值。每一轮你可以选择是否用魔法值(若不用则...

【NOIP 模拟题】[T1]连锁店(贪心)

白纱凝霜,梦萦千回

NOIP模拟题 2016.11.5 [贪心] [坐标离散化] [循环序列LIS]

T1: 题意:大天使之剑。。有平A、重击、群击三种攻击方式,伤害分别是1、2、1。,要让所有的小兵GG,问最少需要受多少伤害。由于群击在人数大于等于3的时候占优势,那么这种情况优先考虑群击,小于等于...

NOIP模拟题 2016.10.13 [贪心] [记忆化搜索]

T1: 题意: 给定一个操作序列,问这个数据结构可能是哪些? 模拟即可。记得判空!!因为可能是不知名的神奇数据结构。#include #include #include #include #in...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:NOIP模拟题 2016.8.27 [贪心] [DP] [计数问题]
举报原因:
原因补充:

(最多只允许输入30个字)