2017.10.16队内互测——胡策

来源:互联网 发布:跨平台app开发 知乎 编辑:程序博客网 时间:2024/05/22 00:48

出题人:Byike,RC,lxt ,Q
本套题死宅气息浓厚…

Problem 1 :埃罗芒阿老师

题目来源:http://codevs.cn/problem/2913/

题目描述

埃罗芒阿老师是著名的插画家, 她的工作是为
电击文库出版的的书画插画。快要到截稿日了,埃罗芒阿老师还在水>_<埃罗芒阿突然发现自己还有一大堆插画没有完成,如果不能在截稿时间内完成是要扣工资的。
于是埃罗芒阿老师把每个任务所需的时间和现在(0 时刻)距离每个任务截稿的时间记录了下来,想要计算出最多可以完成多少任务。
输入描述
第一行是一个整数 N,接下来 N 行每行两个整数 T1,T2 描述一个任务: 完成这个任务需要 T1 秒,如果在 T2 秒之内还没有完成任务,这个任务就到截稿时间了。
输出描述
输出一个整数 S,表示最多可以完成 S 个任务.
样例输入
4
100 200
200 1300
1000 1250
2000 3200
样例输出
3
数据范围及提示
对于 30%的数据, N≤100;
对于 60%的数据, N≤10000;
对于 80%的数据, N < 150,000; T1 < T2 <
INT_MAX;
对于 100%的数据, N < 150,000; T1 < T2 <
LLONG_MAX;
所有数据保证随机生成。
思路
按照截稿日期从小到大排序,截稿日期相同则按耗费时间从小到大排序。记录当前选取任务的总时间sum,每次对于一个任务,若由sum结束时间到截稿时间间允许完成这项任务则直接加入答案,若不能则取出已选择任务中耗费时间最多的任务,如果其耗费时间大于当前任务则与之交换,即选择当前的而放弃耗费时间最大的,这里用堆来维护。
贪心策略:尽量使得后面任务的剩余时间多,可证

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>using namespace std;int n;unsigned long long sum,ans;struct ren{    unsigned long long s,t;    bool operator < (ren a)const    {        return s<a.s;    }}l[200010];priority_queue<ren>q;bool cmp(ren a,ren b){    return a.t==b.t?a.s<b.s:a.t<b.t;//写法}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    scanf("%I64d%I64d",&l[i].s,&l[i].t);    sort(l+1,l+n+1,cmp);    for(int i=1;i<=n;i++)    {        if(l[i].t-sum>=l[i].s)        {            q.push((ren){l[i].s,l[i].t});            sum+=l[i].s;            ans++;        }        else        {            ren k=q.top();            if(k.s>l[i].s)//不会使后面剩余空间变小             {             //不可以写为if(l[i].t-(sum-k.s)>l[i].s),有可能会使后面剩余空间变小                q.pop();                q.push((ren){l[i].s,l[i].t});                sum-=k.s;                sum+=l[i].s;            }        }    }    printf("%I64d",ans);    return 0;}

Problem 2 :名侦探柯南

题目来源:http://codevs.cn/problem/1089/

题目描述

铃木次吉郎又一次向基德发出挑战,而这次的挑战是在铃木号快车上,这辆一年只运行一次的快车上,乘客几乎是固定不变的,尤其是 7 号车厢,这节车厢里的乘客每年都提前预定,而这么做的理由也只是为了参加在这辆车上独有的推理谜题,不幸的是,因为今年毛利小五郎的出现,在这节车厢中出现真的杀人事件,现在为了找出凶手,车厢中的人被聚集到一起,共有 N 个人,他们一共会说 P 句证词,但N 个人中会有 M 个人说谎, 但凶手只有一个,因为柯南还在寻找其他证据,为此你要通过他们说的话去判断凶手是谁。
输入描述
输入第一行为 3 个数, N,M,P,分别表示有 N 个人, M 个人说谎, P 句证词以下 N 行,每行一个人名(全部大写)之后 P 行,每行开始为一个人名,后紧跟一个冒号和一个空格,后面是一句证词(符合表中所列的格式,可能有废话)证词每行不超过 250 个字符输入中不会出现连续的两个空格,且每行开头和结尾也没有空格。
这里写图片描述
单词注明: guilty
角色注明:
铃木次吉郎:铃木财团顾问, 爱好环游世界,在得知有关基德的事
件后, 扬言要亲手逮捕基德(用自家的宝石来作为诱饵)
基德(KID):本名黑羽快斗,为调查父亲死亡真相而成为怪盗寻找
珍稀的宝石, 以此找出幕后真相, 也以铃木拿宝石挑战作契机寻找
宝石(详情请见怪盗基德 1412)
江户川柯南:原名工藤新一(滚筒洗衣机), 在服用酒厂药物后变小
而以柯南为名掩盖自己未死的真相
毛利小五郎:一直划水的侦探
输出描述
输出只有一行, 有三种情况:
1 , 若确定一个凶手,则输出凶手名字
2 , 找到符合条件的凶手, 但有多个,输出
“Cannot Determine” (不含引号)
3 , 没有找到符合条件的凶手, 即根据已知条件不能
确定任何一个可能的凶手,输出“Impossible” (不
含引号)。
样例输入
3 1 5
MIKE
CHARLES
KATE
MIKE: I am guilty.MIKE: Today is Sunday.
CHARLES: MIKE is guilty.
KATE: I am guilty.
KATE: How are you??
样例输出
MIKE
数据范围及提示
1<=N<=20 , 0<=M<=N ,1<=P<=100
数据保证不随机生成。
凶手保证在 N 个人中,但他/她不一定说过话。
思路
枚举犯人枚举今天是星期几,若合法则更新答案,但实现实在是恶心…代码能力题

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<map>using namespace std;int n,m,p,t,f,ans;int tf[1010];string s,now;string num[1010];struct inf{    int id;//说话者的编号     string sy;//证词 }fl[1010];map<string,int>name;//将人名映射到编号 const string days[]={    "Today is Monday.",    "Today is Tuesday.",    "Today is Wednesday.",    "Today is Thursday.",    "Today is Friday.",    "Today is Saturday.",    "Today is Sunday.",};bool check(int a,int b){    if(tf[a]==-1)    {        if(!b)        f++;        else t++;        if(f>m)//超过给定人数不合法         return 1;        if((n-t)<m)//同理         return 1;    }    if(tf[a]==-1)    {        tf[a]=b;//记录         return 0;    }    else    {        if(tf[a]==b)        return 0;        else return 1;//与之前的情况冲突     } }void solve(int k,string day){    memset(tf,-1,sizeof(tf));//记录每人说的是真话1还是假话0     t=f=0;//t为未说谎的人数,f为说谎的人数     for(int i=1;i<=p;i++)    {        int pos;        pos=fl[i].sy.find("I am guilty.");        if(~pos)//pos!=EOF但不可以写为pos!=0         {            if(check(fl[i].id,fl[i].id==k))            return;//当前状态不合法便退出         }        pos=fl[i].sy.find("I am not guilty.");        if(~pos)        {            if(check(fl[i].id,fl[i].id!=k))            return;        }        pos=fl[i].sy.find(" is guilty.");        if(~pos)        {            now=fl[i].sy;            now.erase(pos,11);            int b=name[now];            if(check(fl[i].id,b==k))            return;        }        pos=fl[i].sy.find(" is not guilty.");        if(~pos)        {            now=fl[i].sy;            now.erase(pos,15);            int b=name[now];            if(check(fl[i].id,b!=k))            return;        }        pos=fl[i].sy.find("Today is ");        if(~pos)        {            if(check(fl[i].id,fl[i].sy==day))            return;        }    }    if(ans&&ans!=k)    {        printf("Cannot Determine");        exit(0);//终止程序     }    ans=k;}int main(){    scanf("%d%d%d",&n,&m,&p);    for(int i=1;i<=n;i++)    {        cin>>num[i];        name[num[i]]=i;    }    for(int i=1;i<=p;i++)    {        cin>>s;        s.erase(s.length()-1,1);//删除':'        fl[i].id=name[s];        getline(cin,fl[i].sy);        fl[i].sy.erase(0,1);    }    for(int i=1;i<=n;i++)//枚举犯人     for(int j=0;j<=6;j++)//枚举今天是星期几     solve(i,days[j]);    if(!ans)    printf("Impossible");    else    cout<<num[ans];    return 0;}

Problem 3 :

题目来源:https://www.luogu.org/problem/show?pid=2246#sub

题目描述

一天, 翻阅 Dark Flame Master 黑暗笔记的邪王真眼使发现了笔记中所记载的不可視境界線的秘密。在黑暗笔记的某一页, 她看见了一篇文章。 这篇文章里记载着寻找不可視境界線并前往异世界的方法。
这篇文章由英文字母、和空白字符(制表/空格/回车)构成,但由于管理局的介入,字母的大小写变得十分混乱,换行和空格也不成章法。
Dark Flame Master 告诉邪王真眼使,这篇文章中其实隐含着一个数字 x,只要朝向不可視境界線并说出 x 次“闇の炎に抱かれて消えろっ! ”就可以打开异世界的通道, 这个 x 就是”HelloWorld” 在文章中作为子序列出现的次数。
于是邪王真眼使重新阅读了笔记并解放了“じゃおう シンガン” 的力量!
由于解放后的“じゃおう シンガン” 力量十分强大, 所以大小写对于她而言毫无区别;因此, “hEllOWorLD” 这样的子序列是可以的。 所有的空格回车和制表符都可以被她直接忽略掉;也就是说,“HelloWorld” 是可以的; 当然, “hE Llow oR ld” 也是可以的。
现在,借助邪王真眼使的力量, Dark Flame Master 需要帮助她计算出在这篇文章中“HelloWorld” 作为子序列出现的次数。由于答案可能很大, 请输出结果对 1000000007(10^9+7)的余数。
输入描述 输入包含若干行。这些行的内容共同构成一篇文章(由于管理局的介入, 十分可能出现语法不通顺的情况) 。文章以 EOF(文件结尾)结束。
输出描述 输出仅包含一个整数,表示这篇文章中“HelloWorld” 出现的次数。
样例输入
1
HhEeLlLlOoWwOoRrLlDd
样例输出
1
1536
样例输入
2
Gou Li Guo Jia Sheng Si Yi
Qi Yin Huo Fu Bi Qu Zhi
River can feed people
Also can race boats
Hall Ellen Ok Words locked
样例输出
2
273
数据范围及提示
记 n 为输入的文章的长度(字符数)。
对于 20%的数据, n <= 20。
对于 50%的数据, n <= 500。
对于所有的数据, 15 <= n <= 500000。
数据不保证随机生成。 (╯▽╰)
被漆黑烈焰吞噬殆尽吧!
思路
简单处理一下输入,方法 1:设置DP数组f[i][j]表示字符串匹配到i位置,HELLOWORLD(模板串)匹配到j位置的方案数。将无关字母忽略,对于只存在模板串中字母的情况,转移为:
f[i][j]=f[i-1][j];
if(s[i]==t[j])
f[i][j]+=f[i-1][j-1];

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int mod = 1000000007;int len;int f[500010][15];string s,t;int main(){    s+="*";//或者为'*',使string储存开始下标为1      while(cin>>t)//与scanf写法不同     s+=t;//string的方便之处     t="*HELLOWORLD";    len=s.length()-1;    for(int i=1;i<=len;i++)    if(s[i]>='a')//转换为大写字母     s[i]-=32;    for(int i=0;i<=len;i++)    f[i][0]=1;//初始化为1,使得在之后的更新中可以加上字母本身长度1数量    for(int i=1;i<=len;i++)//二维,i-1状态的所以情况都已确定     for(int j=1;j<=10;j++)    {        f[i][j]=f[i-1][j];//字符串匹配到i的位置,HELLOWORLD匹配到其中j的位置的方案数首先等于前一位置匹配到j位置的方案数         if(s[i]==t[j])//若当前位置i可以与j位置匹配,确定位置并过滤无关字符         f[i][j]+=f[i-1][j-1],f[i][j]%=mod;//乘法原理,每一个原串位置在i之前,模板串位置在j之前的字母都会与当前字母形成一个新的方案,累加答案         //不可直接写作%=1e9+7,类型为double无法与int取模     }    printf("%d",f[len][10]);    return 0;}

方法 2:设置DP数组f[i]表示以i号位置字母(模板串)结尾的子序列的方案数,由于少了一维,我们无法通过前一维找到可用来转移的前一状态,于是对i倒序枚举,使得以i位字母在原串位置之前的所有字母结尾的方案数都已求出后才更新i位,保证由前向后转移
转移时,对于f[i],在之前的串中出现时更新的方案数作为这一次的基数,然后再加上以前一位字母(模板串)结尾的子序列方案数即:
f[i]=f[i]+f[i-1];
原理:f[i-1]包含了f[1~i-1]的答案

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int mod = 1000000007;char ch;int f[1010];int main(){    char t[12]="*helloworld";//11太小..好像应该空出1位来     f[0]=1;//初始化     while((ch=getchar())!=EOF)//不可写作~ch=getchar(),注意(ch=getchar())加括号...     {        for(int i=10;i>=1;i--)//倒序枚举         {            if(ch==t[i]||ch+32==t[i])//f[i-1]包含有结尾字母位置为1-i-1的子序列的方案数             f[i]=(f[i]+f[i-1])%mod;//以i位置字母结尾的子序列的方案数=之前已有的方案数+以i-1位置字母结尾的子序列的方案数         }    }    printf("%d",f[10]);    return 0;} 

Problem 4 :

题目来源:https://www.luogu.org/problem/show?pid=3927

题目描述

银桑、神乐、新八三人在测试阿姆斯特朗回旋加速喷气式阿姆斯特朗炮的威力。
阿姆斯特朗回旋加速喷气式阿姆斯特朗炮十分神奇, 使用方式如下:
输入两个数字 n,k 到阿姆斯特朗回旋加速喷气式阿姆斯特朗炮的控制台中,然后阿姆斯特朗回旋加速喷气式阿姆斯特朗炮会计算出 n!并把它转化为 k 进制。最后 n!在 k 进制下末尾 0 的个数就是本次发射的威力,每个 0 代表 1 点威力。
为了测试时不造成太大的破坏,三人想知道每次测试,发射的威力有多大。
现在给出多组测试的 n 和 k, 请计算出每次发射的威力。
输入描述
输入文件为 amstl.in
题目包含多组数据, 以 EOF(文件结尾)为结束。所有题目数据保证不包含样例。
long long 类型输入输出请用%I64d。
对于每组数据,输入一行两个正整数 n,k;
输出描述
输出文件为 amstl.out
每组数据一行,包含一个整数,表示本次发射的威力。
样例输入
10 40
样例输出
2
数据范围及提示
对于 30%的数据, n <= 1000000, k = 10
对于另外 10%的数据, n <= 20, k <= 20
对于另外 20%的数据, n <= 50, k <= 52
对于另外 10%的数据, n<=10^12, k = 2
对于 100%的数据, n <= 10^12, k <= 10^12
思路
对于一个数转k进制数,在前数%k==0时(%后前数/k)使得末尾0的个数加一。
于是考虑对n!和k进行质因数分解,对n!进行质因数分解即对1~n中的数进行质因数分解——原理同之前的高校模拟赛:赛小城学数学 http://blog.csdn.net/qq_36693533/article/details/78218929
则答案为k的某一质因子(在1-n中的出现次数/在k中的出现次数)的最小值,即为k被n!整除的次数

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll;ll n,ns,k,cnt1,cnt2,ans;int main(){    while(~scanf("%lld%lld",&n,&k))    {        ans=1e17+7;        for(int i=2;i<=sqrt(k)+1;i++)//至多不大于根号n         {            ns=n;//注意n的值不可以随着循环改变             cnt1=0;            cnt2=0;            while(k%i==0)            cnt1++,k/=i;            if(cnt1)            {                ll base=i;                while(ns)                cnt2+=(ns/base),ns/=base;//ns/base:1~ns中包含有base这个质因子的数的个数                 ans=min(ans,cnt2/cnt1);//将k中某一质因子的个数在n中的倍数取min             }        }        if(k>1)//当前k为一质因子         {            cnt2=0;            while(n)            cnt2+=(n/k),n/=k;            ans=min(ans,cnt2);        }        if(ans==1e17+7)        ans=0;        printf("%lld\n",ans);    }    return 0;}
原创粉丝点击