2017.1.19日测试题解

ProblemA:智乃

题目描述:

给你N个字符串,你可以选择其中一个字符串的一段前缀进行翻转,但是你必须保证这个前缀的长度是偶数。你可以进行无限次这样的操作,并且如果两个字符串变得相同的时候,你就可以把这两个字符串都删除掉,问最后最少升多少个字符串。
T组测试数据。
N=50,T=11,串长不超过50

题目分析:

  1. 很容易得到如果两个串能互相消掉的话一定是长度相同的;
  2. 因为每次翻转的必须是前缀而且前缀的长度必须是偶数,所以我们发现如果一个串的长度是奇数的话,那它的最后一位就被固定动不了了;
  3. 还可以发现无论怎么变幻第一个字符和第二个字符都是相邻的,第三个字符和第四个字符都是相邻的,第五个字符和第六个字符都是相邻的……
  4. 如果每次翻转的前缀的长度不限定奇偶的话,我们可以用这些字母构造出包含这些字母的任意的串(意思就是如果我们把相邻的两个字符组成一个二元组,这些二元组就可以任意调换顺序,都可以由原串变幻而来)。

根据以上的信息我们可以把一个串按照这样的方式存储:
字符的长度(len),如果串的长度是奇数的话最后一位是什么(alone),从头开始每相邻的两个字符组成一个二元组,把每个二元组哈希成一个值(二元组内的两个字符可以调换顺序,所以hash的时候这两个字符应该保证第一个字符小于等于第二个字符或者第一个字符大于等于第二个字符),对这些二元组进行排序。

然后我们对所有的串按照alone第一关键字,len第二关键字,二元组第三关键字排序。
每次比较相邻的两个串是否相同,如果相同,就减掉两个串,然后再比较下两个串。
时间复杂度……爱多少多少,N=50,T=11,串长不超过50,怎么都过。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct QAQ{
    int len,alone;
    int couple[30];
    bool operator == (const QAQ &c) const
    {
        if(alone!=c.alone || len!=c.len) return false;
        for(int i=1;i<=len;i++)
            if(couple[i]!=c.couple[i]) return false;
        return true;
    }
    bool operator < (const QAQ &c) const
    {
        if(alone < c.alone) return true;
        if(alone > c.alone) return false;
        if(len < c.len ) return true;
        if(len > c.len ) return false;
        for(int i=1;i<=len;i++)
        {
            if(couple[i]<c.couple[i]) return true;
            if(couple[i]>c.couple[i]) return false;
        }
        return false;
    }
}a[60];
int T,n,ans;
char s[60];
int main()
{
    scanf("%d",&T);
while(T--)
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);

        int len=strlen(s+1);
        a[i].len=len>>1;
        if(len&1) a[i].alone=s[len];
        else      a[i].alone=0;
        memset(a[i].couple,0,sizeof(a[i].couple));
        for(int j=1;j<=a[i].len;j++)
        {
            if(s[j*2-1]>s[j*2]) swap(s[j*2-1],s[j*2]);
            a[i].couple[j]=s[j*2-1]*1000+s[j*2];
        }
        sort(a[i].couple+1,a[i].couple+1+a[i].len);
    }
    sort(a+1,a+1+n);
    ans=n;
    for(int i=1;i<n;i++)
        if(a[i]==a[i+1]) ans-=2,i++;
    printf("%d\n",ans);
}
    return 0;
}

Problem B:麻耶

题目描述:

油库里是幻想乡特有的一种生物。每只油库里都有一个战斗力值和一个能量值。当两只油库里战斗时,总是战斗力值高的一位获胜。获胜者的战斗力值将变成(自己的原战斗力值-对手的战斗力值+对手的能量值)。败者将死去。若两者战斗力值一样,则同归于尽。
思考熊发现了很多油库里,他想知道通过互相战斗之后油库里中战斗力值+能量值最高的一个可能达到多少。你能帮他们求出来么?(假设除了考察的那只油库里外,其他油库里之间不会发生战斗)
油库里最多100000只,每个油库里的战力值和能量值大于等于1,小于等于10^9。

题目分析:

我们定义每只油库里的贡献值为它的能量值减去它的战斗力值。
那么贡献值为负或0的油库里我们肯定不会去打。
我们把油库里按照油库里的贡献值分为两堆food(贡献值为正)和soldier(贡献值为负)。

我们考虑如果我们考察的油库里是food中的。我们假设food中的一只油库里可以打败其他所有的油库里,那么它的答案应该是: 所有food中油库里的贡献和 + 自己的战力值和能量值 - 自己的贡献值。
化简之后就是:所有food中油库里的贡献和+2*自己的战力值。
那么显然food中战力值最大的油库里最后的答案最大,而且战力值最大的油库里一定能打倒其他所有的food油库里,所以food中我们就选择 战力值最大的那只更新答案就可以了。

再来考虑soldier这一堆,我们可以贪心的想到,我每次去food堆里找一只战力值最小的去打,因为这样既能打过,又能得到加成,一定是最优方案。
所以我们把food按照战力值排序。
我们用up数组代表贡献值的前缀和。
我们维护一个lim数组,lim[i]代表我想要得到第i个油库里的贡献最开始的战力值最少是多少。
可以推出来lim[i]= max(lim[i-1],第i个food的战力值-up[i-1])。
(意思就是我想要打倒一个,前提是我已经打倒了它前面的一只,并且我的原始战力值加上我得到的加成要大于这只油库里的战力值)
这样在处理每只soldier时,在lim数组中二分寻找答案,然后更新答案即可。
时间复杂度O(nlogn)

代码如下:

#include<cstdio>
#include<algorithm>
#define N 120000
using namespace std;
inline long long Max(long long x,long long y) { return x>y?x:y; }
struct Yokuri{
    long long fight,energy;
    Yokuri(long long x=0,long long y=0):fight(x),energy(y){}
    bool operator < (const Yokuri &c) const {return fight<c.fight;}
}food[N],soldier[N];
long long lim[N],up[N],ocm[N];
long long ans=-2147483647;
int supply,top;
int n,x,y;
long long judge(int l,int r,long long fight)
{
    int mid,ans=l-1;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(fight>lim[mid]) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return up[ans];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(y-x>0) food[++supply]=Yokuri(x,y);
        else      soldier[++top]=Yokuri(x,y);
    }
    sort(food+1,food+1+supply);
    lim[0]=-2147183647;
    for(int i=1;i<=supply;i++)
    {
        lim[i]=Max(lim[i-1],  food[i].fight-up[i-1]);
        ocm[i]=Max(lim[i-1],food[i+1].fight-up[i-1]);
        up[i]=up[i-1]+food[i].energy-food[i].fight;
    }
    for(int i=1;i<=top;i++)
     ans=Max(ans,soldier[i].energy+soldier[i].fight+judge(1,supply,soldier[i].fight));
    ans=Max(ans,food[supply].fight*2+up[supply]);
    printf("%lld\n",ans);
    return 0;
}

Problem C:惠

题目描述:

现在你要实现一个文件系统,支持以下操作

cd Directory_Name
如果当前路径下有名为 Directory_Name 的文件夹,则进入该文件夹所对应
的路径,否则输出“No such directory!”。

cd ..
如果当前路径存在父路径,则返回父路径, 否则输出“No parent directory!” 。

touch File_Name
如果当前目录下存在名为File_Name 的文件则输出“File already exists!” ,
否则创建这样一个文件。

rm File_Name
如果当前目录下存在名为 File_Name 的文件则删除它,否则输出“No such
e!” 。

mkdir Directory_Name
如果在当前路径下存在名为 Directory_Name 的文件夹,则输出“Directory
ready exists!” ,否则创建这样一个文件夹(当前路径不变) 。

rmdir Directory_Name
如果在当前路径下存在名为 Directory_Name 的文件夹,则删除之,否则输
出“No such directory!” 。

ls
列出当前路径下所有的文件和文件夹,每一项占一行,按创建的先后顺序给
出。采用以下形式输:
“Item_Name Type”
Type 为(文件夹)或(文件)

注意:同一路径下文件与文件夹可以同名,但同一路径下文件与文件、文件
夹与文件夹不能同名。
初始时当前路径处于根路径下,无父路径。

题目分析:

大数据结构,码农模拟题。

代码如下:

#include<cstdio>
#include<map>
#include<vector>
#include<iostream>
#include<string>
#include<iterator>
using namespace std;
const char error[6][50]={
"No such directory!",
"No parent directory!",
"File already exists!",
"No such file!",
"Directory already exists!",
"No such directory!"
};

struct paper{
    string s;
    char c;
    paper(string s,char c):s(s),c(c){}
    bool operator == (const paper &x) {return s==x.s && c==x.c;}
};
struct docu{
    map<string,docu*> sub;
    vector<paper> file;
    docu *fa;
    docu(docu *x=0x0):fa(x)
    {
        sub.clear();
        file.clear();
    }
}*root=new docu;

void cd(string s)
{
    if(s[0]=='.')
    {
        if(root->fa) root=root->fa;
        else         cout<<error[1]<<endl;
    }
    else
    {
        if(root->sub[s]) root=root->sub[s];
        else             cout<<error[0]<<endl;
    }
    return;
}
void mk(string s,char c)
{
    paper tmp=paper(s,c);
    bool judge = false;
    vector<paper> :: iterator it;
    for(it=root->file.begin();it!=root->file.end();it++)
        if((*it)==tmp) {judge=true;break;}
    if(judge)
    {
        if(c=='D') cout<<error[4]<<endl;
        else       cout<<error[2]<<endl;

    }
    else
    {
        root->file.push_back(tmp);
        if(c=='D')
        {
            docu *newly_build=new docu(root);
            root->sub[s]=newly_build;
        }
    }
    return;
}

void rd(string s,char c)
{
    paper tmp=paper(s,c);
    bool judge = false;
    vector<paper> :: iterator it;
    for(it=root->file.begin();it!=root->file.end();it++)
        if((*it)==tmp) {judge=true; break;}
    if(judge)
    {
        root->file.erase(it);
        if(c=='D') root->sub[s]=NULL;
    }
    else
    {
        if(c=='D') cout<<error[5]<<endl;
        else       cout<<error[3]<<endl;
    }
    return;
}

void ls()
{
    vector<paper> :: iterator it;
    for(it=root->file.begin();it!=root->file.end();it++)
        cout<<it->s<<" <"<<it->c<<">"<<endl;
    return;
}

int Q;
string s,opt;
int main()
{
    cin>>Q;
    for(int i=1;i<=Q;i++)
    {
        cin>>opt;
        if(opt[0]=='l') ls();
        else
        {
            cin>>s;
            if(opt[0]=='c') cd(s);
            if(opt[0]=='t') mk(s,'F');
            if(opt[0]=='m') mk(s,'D');
            if(opt[0]=='r')
            {
                if(opt[2]=='d') rd(s,'D');
                else            rd(s,'F');
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值