CF Educational Round 19 797 (F题DP+RMQ)以后补

来源:互联网 发布:淘宝 诚信荣誉 编辑:程序博客网 时间:2024/06/05 18:01

A题 http://codeforces.com/problemset/problem/797/A

因子分解即可,三分钟写完wa了半个小时,代码写的搓。

以此输出0.....k-2。k-1==n/已经输出的积(特判一下k==1的情况)。

#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=200100;struct node{    int a;    int b;}vis[maxn];int main(){    memset(vis,0,sizeof(vis));    int n,k;    scanf("%d%d",&n,&k);    int t=1; int sum=0; int y=n;    int cnt=0;    while(n>1)    {        ++t; if(n%t) continue;        int flag=0;        while(n%t==0)        {            n=n/t; flag++;            sum++;        }        vis[cnt].a=t;  vis[cnt].b=flag; cnt++;    }    if(sum<k) { printf("-1\n"); return 0;}    else if(sum==k)    {        printf("%d",vis[0].a);        vis[0].b-=1;        for(int j=0;j<cnt;j++)        {            while(vis[j].b!=0)            {                printf(" %d",vis[j].a); vis[j].b--;            }        }        printf("\n");    }    else    {        if(k==1)        {            printf("%d\n",y); return 0;        }        int ans=1; int t=0;        printf("%d",vis[0].a);        vis[0].b--; t++; ans*=vis[0].a;        for(int j=0;j<cnt;j++)        {            if(t==k-1) break;            while(vis[j].b!=0)            {                printf(" %d",vis[j].a); t++; ans*=vis[j].a; vis[j].b--;                if(t==k-1) break;            }            if(t==k-1) break;        }        if(t==k-1) {printf(" %d\n",y/ans);}        else printf("\n");    }    return 0;}

B http://codeforces.com/problemset/problem/797/B

把所有正整数(x)加起来(同时如果x是奇数,把-x扔到优先队列中)(别忘了有些数字开始就是负奇数也要扔到队列里),如果是奇数直接输出,偶数弹出优先队列的队列首(从大到小)相加,输出即可!

#include <iostream>#include<stdio.h>#include<algorithm>#include<queue>#include<string.h>using namespace std;const int maxn=1e5+10;priority_queue<int> fj,fo;typedef long long ll;int main(){    int n;    scanf("%d",&n);    ll ans=0;    for(int i=0;i<n;i++)    {        int x;        scanf("%d",&x);        if(x>0)        {            ans+=x;            if(x%2) fj.push(-x);            else fo.push(-x);        }        else        {            if(x%2) fj.push(x);            else fo.push(x);        }    }    if(ans%2) printf("%I64d\n",ans);    else    {        printf("%I64d\n",ans+fj.top());    }    return 0;}



C http://codeforces.com/problemset/problem/797/C

题意看了半天,就是两种操作 

1. 把串s的首字母扔到t中 

2. 把串t中的最后一个字母扔到u中 (不难发现串t其实就是一个栈)

aim:使u的字典序最小

 做法如下

1 首先扫一遍s串把每个字母在s串中的出现次数算出来(vis数组)

2 再扫一遍

    2-1.如果栈t为空把s[i]塞入栈 ,vis[s[i]-'a']--(s串中这个字母被取出);

    2-2.如果栈不为空

          1.此时s串中没有比栈顶的元素更小的(0(1)在vis数组找一遍),弹出栈顶,加入到u串中。

          2.如果有比栈顶的更小的,把s[i]塞入栈 ,vis[s[i]-'a']--(s串中这个字母被取出);

3别忘了,当i到n的时候 栈有可能里面还有元素,以此加入u串中

4输出


#include <iostream>#include<stdio.h>#include<algorithm>#include<string.h>#include<stack>using namespace std;const int maxn=1e5+120;char s[maxn];stack<char> mys;char ans[maxn];int vis[30];int main(){    memset(vis,0,sizeof(vis));    memset(ans,0,sizeof(ans));    scanf("%s",s);    int n=strlen(s); int cnt=0;    int i=0;    for(int i=0;i<n;i++)    {        vis[s[i]-'a']++;    }    i=0;    while(i<n)    {        if(mys.size()==0)        {            mys.push(s[i]); vis[s[i]-'a']--; i++;        }        else        {            int t=mys.top()-'a';            bool tt=0;            for(int j=0;j<t;j++)            {                if(vis[j]) tt=1;            }            if(tt)            {               mys.push(s[i]); vis[s[i]-'a']--; i++;            }            else            {                ans[cnt++]=mys.top(); mys.pop();            }        }    }    while(mys.size())    {       ans[cnt++]=mys.top(); mys.pop();    }    for(int i=0;i<cnt;i++)    {        printf("%c",ans[i]);    }    printf("\n");    return 0;}


D http://codeforces.com/problemset/problem/797/D

英语渣题意迷了好久,意思就是有1-n个树的节点 (V-LSON-RSON,其中v是data值,LSON,RSON这里都是节点的下标)。这些节点会构成一个二叉搜索树(左儿子小于根,右儿子大于根)。

aim:有多少个节点存放不合适?

(注意:有的V会出现多次)

思路,

1.首先用vis[]数组把出现左右儿子的节点下标都记录一下了。再扫一遍1-n,没有做过标记的就是root。

2.开一个map<int,int>记录下来每个v出现多少次(以后算答案)

3  DFS(now,p,q) p是下界 q是上界;

   DFS(root,-inf,inf) 从root开始依次找不合法的节点(这里用将合法的节点出现次数改为0来实现)

   左儿子DFS(root.lson,p,min(q,now.data)) 因为左儿子要比根小

   右儿子DFS(root.rson,max(p,now.data),q) 因为右儿子要比根大

   (记得判断左右儿子是否存在)

4 做完DFS之后扫一遍树的节点如果map不为0,加到ans中。

                                    (记得把map再改为0,因为后面有可能还有V避免重复)

5 输出

#include <iostream>#include<stdio.h>#include<algorithm>#include<string.h>#include<map>using namespace std;const int maxn=2e5+20;const int inf=1e9+10;struct node{    int data;    int lson,rson;}tree[maxn];//记录树节点的样子bool vis[maxn];//判断那个节点的标号出现过,没有出现过的就是rootmap<int,int> myp;//记录每个节点的值出现的次数,可能一个data出现多次!void dfs(int now,int mi,int ma ){    if(now==-1)   return ;    if(tree[now].data>=mi&&tree[now].data<=ma)    {        if(myp[tree[now].data])    myp[tree[now].data]=0;    }    dfs(tree[now].lson,mi,min(ma,tree[now].data));    dfs(tree[now].rson,max(mi,tree[now].data),ma);}int main(){    myp.clear();    memset(vis,0,sizeof(vis));    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%d%d%d",&tree[i].data,&tree[i].lson,&tree[i].rson);        if(tree[i].lson!=-1) vis[tree[i].lson]=1;        if(tree[i].rson!=-1) vis[tree[i].rson]=1;        myp[tree[i].data]++;    }    int root;    for(int i=1;i<=n;i++) if(!vis[i]) root=i;    dfs(root,-inf,inf);    int ans=0;    for(int i=1;i<=n;i++)    {        ans+=myp[tree[i].data];        myp[tree[i].data]=0; //这个很关键防止再次加上这个节点值的出现次数    }    printf("%d\n",ans);    return 0;}

http://codeforces.com/problemset/problem/797/E


我的做法就是暴力模拟(当然是TLE) 

看了网上的题解,大致就是

本题的入手点在于分别考虑解决问题的难点,以及巧妙地暴力。

  • 在不考虑 n 的数据规模的情况下,可以根据每个查询暴力解出答案(用变量的变化模拟 p 的移动)。
  • 在不考虑 k 的数据规模的情况下,可以进行动态规划。令 d[i][j]      表示当前起始位置为 i , k 为 j 的情况下需要多少步能够走出数组,那么按照 i从大到小的顺序 DP 就能得到答案 d[p][k] 。

但是本题的问题在于 n 和 k 的数据规模必须同时被考虑。在这种情况下,当 k 比较小的时候(比如小于 100 的时候)我们可以动态规划,当 k 比较大的时候,我们可以暴力。

(大致就是k小的时候DP)

dp[j][i]  j表示k ,i表示p 

{

 =dp[j][i+a[i]+j]+1    (当i+a[i]+j<=n时候)

 =1                          (当i+a[i]+j>n时候)

}

代码如下

#include <iostream>#include<cstdio>#include<algorithm>#include<string.h>#include<queue>using namespace std;const int maxn=1e5+10;const int s=330;int  a[maxn];int dp [s+5][maxn];int n,k,p,q;int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    memset(dp,0,sizeof(dp));    scanf("%d",&q);    for(int j=1;j<=s;j++) //j 代表的是k    {        for(int i=n;i>=1;i--) // i是p        {            if(i+j+a[i]>n) dp[j][i]=1;            else dp[j][i]=dp[j][i+a[i]+j]+1;        }    }    while(q--)    {        int p,k;        scanf("%d%d",&p,&k);        if(k<=s)        {            printf("%d\n",dp[k][p]);            continue;        }        else        {            int cnt=0;            while(p<=n)            {                cnt++;                p=p+a[p]+k;            }            printf("%d\n",cnt);        }    }    return 0;}



DP夹杂这暴力啊! (纯DP超内存,纯暴力超时) 大佬们真是厉害啊


F题 看了题解 用大佬们单调队列做+dp做的

本题的入手点在于恰当地选择解决问题的顺序,能设计动态规划算法并使用数据结构进行优化。 
在不考虑任何数据规模的情况下,我们显然可以对每只老鼠考虑它们在哪个洞中,或对每个洞考虑它收容哪些老鼠。如果用那么可以用搜索将问题顺利解决。 
但是显然我们不能不考虑数据规模,在本题的数据规模下,我们或许能够利用以上两种顺序中的一个,通过动态规划加以解决。为了让问题更清晰,我们似乎应该对老鼠和老鼠洞按坐标排序。 
考虑让 d[i][j] 表示安排了前 i 个老鼠洞,前 j 只老鼠都得到妥善安置的最小的距离和。那么状态 (i,j) 由 (i−1,j),(i−1,j−1),……,(i−1,j−c[i]) 中 d 值最小的状态转移过来(其中 c[i] 表示第 i 个老鼠洞的容量)。于是在动态规划的同时做 RMQ 即可。

在思考实现方式的过程中要注意以下几个问题:

  1. 如何用数据结构优化 RMQ (单调队列)?
  2. 如何快速得知前 j 个老鼠到第 i 个洞的距离之和(预处理)?
  3. 如何优化算法的空间复杂度(滚动数组)?
  4. 累加数据会不会出问题(防溢出)?
好多细节都不会就不看了哎! (其实就是DP+RMQ)

但我RMQ只会用个线段树 最多来个ST   

链接:http://blog.csdn.net/liang5630/article/details/7917702

我还是太弱了!!!!!







0 0
原创粉丝点击