OI 2017 吉大冬令营 Day 2

第二天似乎考的不错嘿嘿嘿 但是数据水啊我也没有办法
Day2 知识点::栈、队列(优先队列、单调队列)、堆、并查集

D2T1 OIER密码

问题描述

吉林省的OIER们保密意识很强,总是将信息加密。
有一个仅含小写字母的字符串,我们把它按如下方法加密:
步骤1:把所有连续的相同字母都用一个字母代替。比如 aaabbbb 被替换为 ab。
步骤2:在随机的位置插入两个相同的小写字母。重复 步骤2 很多次。
下面是一个加密的实例
初始字符串 jjjlloooiieerr 执行步骤1 之后变成 jloier
插入 aa 之后变成jloiaaer 插入 bb 之后变成jloiabbaer 插入 ll 之后变成jllloiabbaer
现在我们给定加密后的字符串,求执行 步骤1 之后的字符串是什么。

思路:

栈:步骤一的作用是保证原字符串中并没有两个或以上的连续字符,那我们可以存储字符串,一个字符一个字符的压栈,如果a[i]==a[i-1]的话,就将两个字符都弹出去 最后输出栈内字符即为答案

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[1000100];
char a[1000100];
int tmp;
int main()
{
	scanf("%s",&s[0]);
	int len=strlen(s);
	for(int i=0;i<len;i++)
	{
		a[++tmp]=s[i];
		if(tmp==1)continue;
		if(a[tmp]==a[tmp-1])tmp-=2;
	}
	for(int i=1;i<=tmp;i++)
		printf("%c",a[i]);
	return 0;
}

 

D2T2 投资 

问题描述

吉林省的OIER们投资了一支股票,大家都知道股票有赚有赔,现给出n天里这支股票的涨跌情况,都为整数,涨为正,跌为负。OIER们想知道天数在s到e之间的这只股票涨跌的最大连续和 。

思路:

f[i]是以第i个元素为结尾,并满足题意的最大连续和,那么:
f[i]=max(sum[i]-sum[j])(i-e<=j<=i-s) sum[i]代表a[1]~a[i]的和
所以用单调队列优化DP,使用前缀和
关于时间戳,看代码吧
最后,在求f[i]的时候记录一下最大值即为答案

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,x,y;
int q[110000];
int sum[110000];
int f[110000];
int l=1,r;
int ans=-10000000;
int main()
{
	scanf("%d%d%d",&n,&x,&y);
	for(int i=1;i<=n;i++)
	{
		int tmp;
		scanf("%d",&tmp);
		sum[i]=sum[i-1]+tmp;
	}
	for(int i=x;i<=n;i++)
	{
		while(l<=r&&sum[q[r]]>=sum[i-x])r--;
		q[++r]=i-x;
		while(l<=r&&q[l]<i-y)l++;
		f[i]=sum[i]-sum[q[l]];
		ans=max(ans,f[i]);
	}
	printf("%d",ans);
	return 0;
}

D2T3 学习计划  

问题描述

吉林省OIER们都是爱学习的同学,所以他们想学习更多的课程。长春市教育局开设了“云课程”网络学习平台,一共有n门课程,但这个平台有个弊端,就是每门课程在时间Ti之后将不再开放,并且每门课程要想学会需要Ci的时间,聪明的你帮助吉林省OIER们计划一下,使他们能够学习最多的课程。

思路:

std用的是优先队列,我写了一个并查集2333
貌似可以过?毕竟发现的问题是在分层(n<100)的暴力函数内挂的……
没错,你没看错,我暴力写挂了QAQ
但其实只是数据水啊 (其实极限数据能把我卡到0啊然而 90 嘿嘿嘿)
先贴一下我改过的代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
struct your
{
    int t,c;
}a[100100];
int n,temp,ans;
int f[100100];
int t[100100];
bool cmp(your j,your k)
{
    if(j.c==k.c)return j.t>k.t;
    return j.c<k.c;
}
int find(int x)
{
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
int check(int i)
{
    for(int j=a[i].t;j>=1;)
    {
        if(t[j]!=0) t[j]=i,a[i].c--;
        else 
        {
            int tmp=find(j);
            if(tmp==j)j--;
            else j=tmp;
            continue;
        }
        if(a[i].c==0) 
        {
            ans++;
            return 1;
        }
        j--;
    }
    return 0;
}
void work(int i)
{
    if(check(i)) 
        for(int j=a[i].t;j>=1;j--)
            if(t[j]==i) t[j]=0,f[j]=find(j-1);
    return ;
}
int main()
{
    memset(t,-1,sizeof t);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].t,&a[i].c);
        temp=max(temp,a[i].t);
    }
    sort(a+1,a+n+1,cmp);
    f[0]=1;
    for(int i=1;i<=temp;i++) f[i]=i;
    for(int i=1;i<=n;i++) work(i);
    printf("%d",ans);
    return 0;
}

这道题是贪心无疑 那么我们要怎么贪?
先按学习时间从小到大,若相等则按截止时间从大到小排序,那么我们设立一个t[];(time)
t[]分为3种情况:
t[]=-1 这个时间没有被占用 t[]=0这个时间已经确定被占用了 t[]!=0 && t[]!=1 这个时间还没有确定
以当前截止时间为终点 向前循环找 C[i]个空位,因为无法确定是否有C[i]个空位,标记t[j]=i;
如果能够找到C[i]个,那就再循环一遍将t[j]标记为0
这时候就会用到并查集了 面对t[]已经被标记为0,就可以维护并查集让find(t[])指向t[]前面第一个非0的t[]
然而面对极端数据会TLE (然而在师大OJ上也A了QAQ) 所以我们要考虑别的做法
首先按截止时间从小到大排序,定义一个已用时间变量,然后依次循环处理:如果当前工作耗时+已用时间小于截止时间,证明能干完,累加ans和已用时间并将耗时插入大根堆。否则判断堆顶元素,如果比当前工作耗时更大,则以当前工作耗时替换之,易得这样做合法且一定更优。

附上STD标程

#include<queue> 
#include<cstdio>  
#include<algorithm> 
using namespace std;  
priority_queue<int>q; 
struct your
{ 
    int t; 
    int c; 
}a[100001]; 
bool cmp(your x,your y) 
{ 
    return x.t<y.t; 
}  
int n,ans; 
int tmp;  
int main()  
{ 
    scanf("%d",&n); 
    for(int i=1;i<=n;i++) 
    scanf("%d%d",&a[i].t,&a[i].c); 
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) 
    { 
        if(a[i].c>a[i].t) continue; 
        if(tmp+a[i].c<=a[i].t)  
        { 
            ans++; 
            tmp+=a[i].c; 
            q.push(a[i].c);  
        } 
        else if(a[i].c<q.top()) 
        { 
            tmp-=q.top(); 
            tmp+=a[i].c; 
            q.pop(); 
            q.push(a[i].c);  
        } 
    } 
    printf("%d",ans);
}

至此 Day 2总结完毕23333

发表评论

邮箱地址不会被公开。 必填项已用*标注