10.16 Loi队内胡策 贪心+毒瘤输入+DP+数论

12 篇文章 0 订阅
11 篇文章 0 订阅

内存 128M
时间 每点1s

Problem 1 埃罗芒阿老师

题目来源

http://codevs.cn/problem/2913/

题目描述

埃罗芒阿老师是著名的插画家,她的工作是为电击文库出版的的书画插画。
快要到截稿日了,埃罗芒阿老师还在水>_<
埃罗芒阿突然发现自己还有一大堆插画没有完成,如果不能在截稿时间内完成是要扣工资的。
于是埃罗芒阿老师把每个任务所需的时间和现在距离每个任务截稿的时间记录了下来,想要计算出最多可以完成多少任务。

输入描述
第一行是一个整数N,
接下来N行每行两个整数T1,T2描述一个任务:完成这个任务需要T1秒,如果在T2秒之内还没有完成任务,这个任务就到截稿时间了。

输出描述
输出一个整数S,表示最多可以完成S个任务.

样例输入
4
100 200
200 1300
1000 1250
2000 3200

样例输出
3

数据范围及提示
对于30%的数据,N≤100;
对于60%的数据,N≤10000;
对于100%的数据,N < 150,000; T1 < T2 < INT_MAX;
所有数据保证随机生成。

题解

贪心+堆
正解:先按截稿日期由小到大排序,这样能使截稿晚的能余出时间照顾早的。//显然
①然后从第一个开始,若当前任务所需时间+之前的总时间小于等于当前截稿时间,则扔进堆里,ans++。//能够完成,更新now
若大于,这时要从它前面所有任务中选一个修理时间最长的(记为y)和它比较,
②若当前任务所需时间 > y,则不扔进堆中。//若替换,当前任务仍然不能完成;
③若当前任务所需时间 < y,则把y替换成当前任务。//当前任务能够完成,ans数不变,使得now减小,更加优;
这样能保证时间利用率最高,完成更多任务。

由于不够优的选择会在第三种情况下pop掉,所以答案是最优的;

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=150000+500;
int n,ans,now;
struct node{
    int ned,lst;
}a[N];
priority_queue<node> Q;
bool operator < (node a,node b){
    return a.ned<b.ned;
}
bool cmp(node a,node b){
    return a.lst<b.lst;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].ned,&a[i].lst);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        if(now+a[i].ned<=a[i].lst){
            Q.push(a[i]);
            ans++; 
            now+=a[i].ned;
            continue;
        }
        node u=Q.top();
        if(u.ned<=a[i].ned) continue;
        Q.pop();
        now-=u.ned;
        Q.push(a[i]);
        now+=a[i].ned;
    }
    printf("%d",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个人中,但他/她不一定说过话。

题解

假设我们都读入了
枚举哪个人是罪犯和今天是星期几
扫一遍判断

读入实在是太恶心了

代码

先粘一波std??
主要是输入恶心,运用了很多string的函数

//num记录编号为i的人的名字,name记录名字的编号 
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n , m , p , t , fl , ans = 0;
int tf[25];
string s , now , num[25];
map<string , int>name;
struct st
{
    int id;
    string ta;
}f[105];
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, bool b)
{
    if(tf[a] == -1)
    {
        if(!b) 
        fl ++ ;//说谎的人 
        else t ++ ;//诚实的人 
        if(fl > m)//说谎的人大于m 
            return 1;
        if(n - t < m)//诚实的人大于n-m 
            return 1;
    }
    if(tf[a] == -1)
    {
        tf[a] = b;return 0;//标记这个人的状态 0说谎 1诚实 
    }
    else
    {
        if(tf[a] == b)//没有矛盾 
            return 0;
        return 1;//出现矛盾  一个人既说谎又诚实 
    }
}
void run(int cr , string day)
{
    memset(tf , -1 , sizeof(tf));
    t = fl = 0;
    for(int i = 1 ; i <= p ; i ++)
    {
        int pos;
        pos = f[i].ta.find("I am guilty.");
        //s.find(str)在s中寻找str这个字符串,返回起始位置的迭代器 
        //如果不存在 返回-1  -1取反为0 
        if(~pos)
        {
            if(check(f[i].id , f[i].id == cr)) return ;
        }
        pos = f[i].ta.find("I am not guilty.");
        if(~pos)
        {
            if(check(f[i].id , f[i].id != cr))return ;
        }
        pos = f[i].ta.find(" is guilty.");
        if(~pos)
        {
            now = f[i].ta;
            now.erase(pos , 11);
            int bm = name[now];
            if(check(f[i].id , bm == cr))return ;
        }
        pos = f[i].ta.find(" is not guilty.");
        if(~pos)
        {
            now = f[i].ta;
            now.erase(pos , 15);
            int bm = name[now];
            if(check(f[i].id , bm != cr))return ;
        }
        pos = f[i].ta.find("Today is ");
        if(~pos)
        {
            if(check(f[i].id , f[i].ta == day))return ;
        }
    }
    if(ans&& ans != cr)
    {
        cout<<"Cannot Determine";
        exit(0);
        //exit()通常是用在子程序中用来终结程序用的,使用后程序自动结束,跳回操作系统。
        //exit(0) 表示程序正常退出,exit⑴/exit(-1)表示程序异常退出。 
    }
    ans = cr;
}
int main()
{
    freopen("kid.in","r",stdin);
    freopen("kid.out","w",stdout);
    cin>>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);
        //s.erase(pos,len)删除起始于 pos位置 长度为len的子串 
        f[i].id = name[s];
        getline(cin , f[i].ta);
        //读取整行文本到f[i].ta 
        f[i].ta.erase(0,1);
    }
    for(int i = 1 ; i <= n ; i ++)
    for(int j = 0 ; j <= 6 ; j ++)
        run(i,days[j]);
    if(ans == 0)
    cout<<"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就是”Hello World”在文章中作为子序列出现的次数。
于是邪王真眼使重新阅读了笔记并解放了“じゃおう シンガン”的力量!
由于解放后的“じゃおう シンガン”力量十分强大,所以大小写对于她而言毫无区别;因此,“hEllOWorLD”这样的子序列是可以的。所有的空格回车和制表符都可以被她直接忽略掉;也就是说,“HelloWorld”是可以的;当然,“hE Llow oR ld”也是可以的。
现在,借助邪王真眼使的力量,Dark Flame Master需要帮助她计算出在这篇文章中“Hello World”作为子序列出现的次数。
由于答案可能很大,请输出结果对1000000007(10^9+7)的余数。

输入描述
输入包含若干行。这些行的内容共同构成一篇文章(由于管理局的介入,十分可能出现语法不通顺的情况)。
文章以EOF(文件结尾)结束。

输出描述
输出仅包含一个整数,表示这篇文章中“Hello World”出现的次数。

样例输入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。
数据不保证随机生成。(╯▽╰)
被漆黑烈焰吞噬殆尽吧!

题解

一个水的一笔的DP
bool数组d[i][j]=1,表示i位置的字符在helloworld中的顺序可以是j
f[i][j]表示到i位,长度为j的子序列的方案总数

dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
for(int i=1;i<=tot;i++){
        for(int j=10;j>=1;j--){
            if(d[i][j]){
                f[j]=(f[j]%mod+f[j-1]%mod)%mod;
            }
        }
    }

如果i位字符编号可以是j
f[i-1][j] 它之前的长度为j的方案数可以累加到它
f[i-1][j-1] 它之前长度为j-1的方案数,末尾元素换成了i,是全新的方案,所以也可以累加到它
滚动数组

代码

std写的真棒!!!!
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
const int mod=1000000007;
int main()
{
    freopen("helloworld.in","r",stdin);
    freopen("helloworld.out","w",stdout);
    long long dp[11]={1};
    char c,hello[20]="?helloworld";
    while((c=getchar())!=EOF)
    {
        for(int i=10;i>=1;i--)
            if(c==hello[i]||c+32==hello[i])
                dp[i]=(dp[i-1]+dp[i])%mod;
    }
    cout<<dp[10];
    fclose(stdin);
    fclose(stdout);
    return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=500000+500,mod=1000000007;
char a;
int tot;
long long f[50];
bool d[N][15];
void read(){
    while(scanf("%c",&a)!=EOF){
        if(a>='A'&&a<='Z') a=a+32;

        switch(a){
            case('h'):{
                tot++;
                d[tot][1]=1;
                break;
            }
            case('e'):{
                tot++;
                d[tot][2]=1;
                break;
            }
            case('l'):{
                tot++;
                d[tot][3]=1;
                d[tot][4]=1;
                d[tot][9]=1;
                break;
            }
            case('o'):{
                tot++;
                d[tot][5]=1;
                d[tot][7]=1;
                break;
            }
            case('w'):{
                tot++;
                d[tot][6]=1;
                break;
            }
            case('r'):{
                tot++;
                d[tot][8]=1;
                break;
            }
            case('d'):{
                tot++;
                d[tot][10]=1;
                break;
            }
        }
    }

}
int main(){
    read(); 
    f[0]=1;
    for(int i=1;i<=tot;i++){
        for(int j=10;j>=1;j--){
            if(d[i][j]){
                f[j]=(f[j]%mod+f[j-1]%mod)%mod;
            }
        }
    }
    printf("%lld",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(文件结尾)为结束。
对于每组数据,输入一行两个正整数n,k;

输出描述
输出文件为amstl.out
每组数据一行,包含一个整数,表示本次发射的威力。

样例输入
10 40

样例输出
2
数据范围及提示
0

题解

求n!在k进制下的末尾0的个数

朴素求法
int j=n!;
while(1){
    if(j%k==0) {
        cnt++,j/=k;
        continue;
    }
    break;
}
return cnt;

显然是过不了的
考虑将n!和k进行质因数分解
在n!中k的质因数组合的个数即为末尾0的个数

分解k
可以枚举 2~根k 统计某个质因子的指数cnt1
而且不用判素数
如4,在枚举2时,已经去除了因子中所有的2,不可能分解出4

分解n!
首先只需要分解在k中有的质因数
类似于赛小城学数学的分解方法,logn的分解出n!的某个质因子的个数cnt2

http://blog.csdn.net/loi_lxt/article/details/78044788

**统计质因数的方法:**
f(a)=n/a+n/(a^2)+n/(a^3)+.....+n/(a^k) a^k<=n,a^(k+1)>n
以从1-9中统计2为例(有2的只有2,4,6,8,);
     2   4   6    8
2   √   √   √   √   4
2^2      √       √   2  
2^31
显然,分层统计了2的次数; 

答案在 cnt1/cnt2中取min
注意判断最后k剩下的是一个大于根k的质数的情况

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const long long inf=1e16+8;
long long ans;
long long n,k,cnt1,cnt2;

int main(){
    freopen("amstl.in","r",stdin);
    freopen("amstl.out","w",stdout);
    while(scanf("%lld%lld",&n,&k)!=EOF){
        cnt1=0,cnt2=0;
        ans=inf;

        long long tmp=sqrt(k)+1;
        for(int i=2;i<=tmp;i++){
            cnt1=0,cnt2=0;
            while(k%i==0){
                cnt1++;
                k/=i;
            }
            if(!cnt1) continue;
            long long base=i;
            while(base<=n){
                cnt2+=(n/base);
                base*=i;
            }
            ans=min(ans,cnt2/cnt1);
        }
        cnt1=0,cnt2=0;
        if(k>1){
            long long base=k;
            while(base<=n){
                cnt2+=(n/base);
                base*=k;
            }
            ans=min(ans,cnt2);
        }

        if(ans==inf) ans=0;

        printf("%lld\n",ans);   
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值