NOIP 2017 PJT4~NOIP 2008 TGT2

2169: 跳房子(NOIP 2017 PJT4)

题目描述

跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 n� 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:
玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R� 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d� 。小 R� 希望改进他的机器人,如果他花 g� 个金币改进他的机器人,那么他的机器人灵活性就能增加 g� ,但是需要注意的是,每 次弹跳的距离至少为 11 。具体而言,当 g<d�<� 时,他的机器人每次可以选择向右弹跳的距离为 d−g,d−g+1,d−g+2�−�,�−�+1,�−�+2 ,…, d+g−2�+�−2 , d+g−1�+�−1 , d+g�+� ;否则(当 g≥d�≥� 时),他的机器人每次可以选择向右弹跳的距离为 11 , 22 , 33 ,…, d+g−2�+�−2 , d+g−1�+�−1 , d+g�+� 。
现在小 R� 希望获得至少 k� 分,请问他至少要花多少金币来改造他的机器人。

输入

第一行三个正整数 n� , d� , k� ,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。 接下来 n� 行,每行两个整数 xi,si��,�� ,分别表示起点到第 i� 个格子的距离以及第 i� 个格子的分数。两个数之间用一个空格隔开。保证 xi�� 按递增顺序输入。

输出

共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k� 分,输出 −1−1 。

样例输入 复制

7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

样例输出 复制

2

提示

输入样例 2

7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

输出样例 2

-1
【输入输出样例 1 说明】

22个金币改进后, 小 R 的机器人依次选择的向右弹跳的距离分别为2,3,5,3,4,32,3,5,3,4,3, 先后到达的位置分别为 2,5,10,13,17,202,5,10,13,17,20, 对应1,2,3,5,6,71,2,3,5,6,7 这66 个格子。这些格子中的数字之和1515 即为小 R 获得的分数。

【输入输出样例 2 说明】

由于样例中 77 个格子组合的最大可能数字之和只有 1818 ,无论如何都无法获得2020分

数据规模与约定

本题共 10 组测试数据,每组数据 10 分。
对于全部的数据满足1≤n≤500000,1≤d≤2000,1≤xi,k≤109,|si|<1051≤�≤500000,1≤�≤2000,1≤��,�≤109,|��|<105。
对于第 1,21,2组测试数据, n≤10�≤10;
对于第3,4,53,4,5 组测试数据, n≤500�≤500
对于第6,7,86,7,8 组测试数据, d=1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL inf=1LL<<34;
const int N=500000;
  
    int n,d,x[N+3];
    LL k,s[N+3],f[N+3];
      
bool chk(int g){
    for(int i=1;i<=n;i++)
        f[i]=-inf;
    f[0]=0;
    for(int i=0;i<=n;i++)
    if(f[i]!=-inf){
        int j;
        for(j=i+1;x[j]<x[i]+(d-g)&&j<=n
                &&x[j]<=x[i]+(d+g);j++);
        for(;x[j]<=x[i]+(d+g)&&j<=n;j++)
            f[j]=max(f[j],f[i]+s[j]);
              
    }
      
    LL tmp=-inf;
    for(int i=1;i<=n;i++)
        tmp=max(tmp,f[i]);
          
    return tmp>=k;
      
}
  
int main(){
    scanf("%d%d%lld",&n,&d,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%lld",&x[i],&s[i]);
    x[0]=0;
    int l=0,r=x[n]-d,mid,ans=-1;
    while(l<=r){
        mid=(l+r)>>1;
        if(chk(mid)){
            ans=mid;
            r=mid-1;
              
        }
        else
            l=mid+1;
          
    }
      
    printf("%d",ans);
      
    return 0;
      
}

2197: 对称二叉树(NOIP 2018 PJT4)

题目描述

一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:
1. 二叉树; 2. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。
下图中节点内的数字为权值,节点外的 id�� 表示节点编号。
 


现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。
注意:只有树根的树也是对称二叉树。本题中约定,以节点 T� 为子树根的一棵“子 树”指的是:节点T� 和它的全部后代节点构成的二叉树。

输入

第一行一个正整数 n�,表示给定的树的节点的数目,规定节点编号 1∼n1∼�,其中节点 11 是树根。 第二行 n� 个正整数,用一个空格分隔,第 i� 个正整数 vi�� 代表节点 i� 的权值。 接下来 n� 行,每行两个正整数 li,ri��,��,分别表示节点 i� 的左右孩子的编号。如果不存在左 / 右孩子,则以 −1−1 表示。两个数之间用一个空格隔开。

输出

输出文件共一行,包含一个整数,表示给定的树的最大对称二叉子树的节点数。

样例输入 复制

2 
1 3 
2 -1 
-1 -1

样例输出 复制

1

提示

输入样例 #2

10 
2 2 5 5 5 5 4 4 2 3 
9 10 
-1 -1 
-1 -1 
-1 -1 
-1 -1 
-1 2 
3 4 
5 6 
-1 -1 
7 8

输出样例 #2

3

【输入输出样例 1 说明】
 


最大的对称二叉子树为以节点 22 为树根的子树,节点数为 11。
【输入输出样例 2 说明】
 


最大的对称二叉子树为以节点 77 为树根的子树,节点数为 33。
【数据规模与约定】
共 2525 个测试点。
vi≤1000��≤1000。
测试点 1∼3,n≤101∼3,�≤10,保证根结点的左子树的所有节点都没有右孩子,根结点的右 子树的所有节点都没有左孩子。
测试点 4∼8,n≤104∼8,�≤10。
测试点 9∼12,n≤1059∼12,�≤105,保证输入是一棵“满二叉树” 。
测试点 13∼16,n≤10513∼16,�≤105,保证输入是一棵“完全二叉树”。
测试点 17∼20,n≤10517∼20,�≤105,保证输入的树的点权均为 11。
测试点 21∼25,n≤10621∼25,�≤106。

#include<bits/stdc++.h>
using namespace std;
int w[1000005];
int a[1000005][2];
int s[1000005];
void dfs(int x)//求出以编号x的节点为根的树的节点数量
{
    s[x]=1;//根
    if(a[x][0]!=-1) dfs(a[x][0]),s[x]+=s[a[x][0]];//左子树
    if(a[x][1]!=-1) dfs(a[x][1]),s[x]+=s[a[x][1]];//右子树
}
bool dfs1(int x,int y)
{
    if(x==-1&&y==-1) return 1;//无左右子树,即叶节点,对称
    if(x!=-1&&y!=-1&&s[x]==s[y]&&w[x]==w[y])//左右相应位置比较
    {
        if(dfs1(a[x][0],a[y][1])&&dfs1(a[x][1],a[y][0])) return 1;//左左-右右,左右-右左
    }
    return 0;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);//输入每个节点的权值
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i][0],&a[i][1]);//输入左右孩子
    dfs(1);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(dfs1(a[i][0],a[i][1])) ans=max(ans,s[i]);//求出所有对称二叉树中节点最多的
    }
    printf("%d",ans);
    return 0;
}

2201: 加工零件(CSP-J 2019 T4)

题目描述

凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 n� 位工人,工人们从 1∼n1∼� 编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。
如果 x� 号工人想生产一个被加工到第 L(L>1)�(�>1) 阶段的零件,则所有与 x� 号工人有传送带直接相连的工人,都需要生产一个被加工到第 L−1�−1 阶段的零件(但 x� 号工人自己无需生产第 L−1�−1 阶段的零件)。
如果 x� 号工人想生产一个被加工到第 1 阶段的零件,则所有与 x� 号工人有传送带直接相连的工人,都需要为 x� 号工人提供一个原材料。
轩轩是 1 号工人。现在给出 q� 张工单,第 i� 张工单表示编号为 ai�� 的工人想生产一个第 Li�� 阶段的零件。轩轩想知道对于每张工单,他是否需要给别人提供原材料。他知道聪明的你一定可以帮他计算出来!

输入

第一行三个正整数 n�,m� 和 q�,分别表示工人的数目、传送带的数目和工单的数目。 接下来 m� 行,每行两个正整数 u� 和 v�,表示编号为 u� 和 v� 的工人之间存在一条零件传输带。保证 u≠v�≠�。 接下来 q� 行,每行两个正整数 a� 和 L�,表示编号为 a� 的工人想生产一个第 L� 阶段的零件。

输出

共 q� 行,每行一个字符串 `Yes` 或者 `No`。如果按照第 i� 张工单生产,需要编号为 1 的轩轩提供原材料,则在第 i� 行输出 `Yes`;否则在第 i� 行输出 `No`。注意输出**不含**引号。

样例输入 复制

3 2 6
1 2
2 3
1 1
2 1
3 1
1 2
2 2
3 2

样例输出 复制

No
Yes
No
Yes
No
Yes

提示

输入样例 #2

5 5 5
1 2
2 3
3 4
4 5
1 5
1 1
1 2
1 3
1 4
1 5

输出样例 #2

No
Yes
No
Yes
Yes

【数据规模与约定】
共 20 个测试点。
1≤u,v,a≤n1≤�,�,�≤�。
测试点 1~4,1≤n,m≤10001≤�,�≤1000,q=3�=3,L=1�=1。
测试点 5~8,1≤n,m≤10001≤�,�≤1000,q=3�=3,1≤L≤101≤�≤10。
测试点 9~12,1≤n,m,L≤10001≤�,�,�≤1000,1≤q≤1001≤�≤100。
测试点 13~16,1≤n,m,L≤10001≤�,�,�≤1000,1≤q≤1051≤�≤105。
测试点 17~20,1≤n,m,q≤1051≤�,�,�≤105,1≤L≤1091≤�≤109。

#include <bits/stdc++.h>
   
using namespace std;
   
const int N = 1e5 + 10;
   
int h[N], e[N * 2], ne[N * 2], idx;
int n, m, q;
   
int dist[N][2];
bool st[N][2];
   
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
   
void bfs()
{
    for (int i = 1; i <= n; i++) dist[i][0] = dist[i][1] = 2e9;
    dist[1][0] = 0;
   
    queue<int> q;
    q.push(1);
   
    while (!q.empty())
    {
        int fr = q.front(); q.pop();
   
        for (int i = h[fr]; i != -1; i = ne[i])
        {
            int j = e[i];
               
            if (dist[j][1] > dist[fr][0] + 1)
            {
                dist[j][1] = dist[fr][0] + 1;
   
                if (!st[j][1])
                {
                    st[j][1] = true;
                    q.push(j);
                }
            }
   
            if (dist[j][0] > dist[fr][1] + 1)
            {
                dist[j][0] = dist[fr][1] + 1;
   
                if (!st[j][0])
                {
                    st[j][0] = true;
                    q.push(j);
                }
            }
        }
    }
}
   
int main()
{
    scanf("%d%d%d", &n, &m, &q);
       
    memset(h, -1, sizeof h);
    while (m--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
   
        add(a, b); add(b, a);
    }
   
    bfs();
    if (h[1] == -1) dist[1][0] = 2e9;
   
    while (q--)
    {
        int a, L;
        scanf("%d%d", &a, &L);
           
        if (L >= dist[a][L % 2]) printf("Yes\n");
        else printf("No\n");
    }
   
    return 0;
}

2208: 侦探推理(NOIP 2003 TGT2)

题目描述

明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
 


证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

输入

输入由若干行组成,第一行有三个整数,M(1≤M≤20)�(1≤�≤20)、N(1≤N≤M)�(1≤�≤�)和P(1≤P≤100)�(1≤�≤100);M�是参加游戏的明明的同学数,N�是其中始终说谎的人数,P�是证言的总数。 接下来M�行,每行是明明的一个同学的名字(英文字母组成,没有空格,全部大写)。 往后有P�行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。证词每行不会超过250250个字符。 输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。

输出

如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 "Cannot Determine";如果程序判断出没有人可能成为罪犯,则输出 "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
#include<bits/stdc++.h>
#define debug cout
using namespace std;
const int maxn=30;
string name[maxn];
int ptr[maxn][maxn]; // 1 means is , -1 means not;
int day[maxn]; // means says day.
int day_can_be[maxn]; // can be that day ? 1 means is , -1 means is not.
int gul_can_be[maxn]; // persob i can be guilty or not , 1 means is , -1 means is not;
int may_be_ans[maxn]; // 0,1 means can be answer or not
string buf[1<<10];
map<string,int> person;
int n,m,p,mx,cnt,ans;
inline int getdate(string x)
{
    if(x=="monday.") return 1;
    if(x=="tuesday.") return 2;
    if(x=="wednesday.") return 3;
    if(x=="thursday.") return 4;
    if(x=="friday.") return 5;
    if(x=="saturday.") return 6;
    if(x=="sunday.") return 7;
    return puts("Wrong spelling ! Fuck you!"),-1;
}
inline char convchar(char x)
{
    if( x>='A' && x<='Z' )
        return x-'A'+'a';
    else return x;
}
inline void convstring(string &x)
{
    for(unsigned i=0;i<x.length();i++)
        x[i] = convchar(x[i]);
}
inline char nextchar(int arg=0)
{
    static char buf[1<<10],*st;
    if(arg)
    {
        fgets(buf,1<<10,stdin),st=buf;
        int i;
        for(i=0;i<1<<10&&buf[i];i++)
            if( buf[i]=='\r' )
                buf[i] = '\n';
        buf[i] = '\n';
    }
    return *st++;
}
inline void getline()
{
    cnt = 0;
    char c=nextchar(1);
    cnt = 1;
    while( c != '\n' )
    {
        if(c==' ')
        {
            if( person.find(buf[cnt]) == person.end() )
                convstring(buf[cnt]);
            cnt++;
        }
        else
            buf[cnt] = buf[cnt] + c;
        c = nextchar();
    }
    while( buf[cnt]=="" ) cnt--;
    convstring(buf[cnt]);
}
inline void resbuf()
{
    for(int i=1;i<=cnt;i++)
        buf[i].clear(),
        buf[i].resize(0);
}
inline void explain()
{
    if( buf[2]!="i" && buf[2]!="today" && person.find(buf[2])==person.end() ) return;
    const int id = person[buf[1]];
    if( buf[2]=="today" )
    {
        int dd = getdate(buf[4]);
        if( day[id] && day[id]!=dd )
        {
            ans = -2;
            return;
        }
        day[id] = dd;
    }
    else if( buf[4]=="guilty." )
    {
        int tar = buf[2]=="i" ? id : person[buf[2]];
        if( ptr[id][tar] && ptr[id][tar]!=1 )
        {
            ans = -2;
            return;
        }
        ptr[id][tar] = 1;
    }
    else if( buf[5]=="guilty." )
    {
        int tar = buf[2]=="i" ? id : person[buf[2]];
        if( ptr[id][tar] && ptr[id][tar]!=-1 )
        {
            ans = -2;
            return;
        }
        ptr[id][tar] = -1;
    }
}
inline void reslogic()
{
    memset(day_can_be,0,sizeof(day_can_be));
    memset(gul_can_be,0,sizeof(gul_can_be));
}
inline void logic(int x,int mul,int& flag)
{
    if(day[x])
    {
        if( day_can_be[day[x]] && day_can_be[day[x]]!=mul )
        {
            flag=0;
            return;
        }
        day_can_be[day[x]] = mul;
    }
    for(int i=1;i<=n;i++)
        if(ptr[x][i])
        {
            const int pp = ptr[x][i]*mul;
            if( gul_can_be[i] && gul_can_be[i]!=pp )
            {
                flag = 0;
                return;
            }
            gul_can_be[i] = pp;
        }
}
inline bool judgedate()
{
    int ret=0;
    for(int i=1;i<=7;i++)
        if( ~day_can_be[i] )
            ret += day_can_be[i];
    return ret<2;
}
inline void judgegul()
{
    int pos = -1,siz=n;
    for(int i=1;i<=n;i++)
    {
        if( !~gul_can_be[i] ) --siz;
        else if( gul_can_be[i] == 1 )
        {
            pos = i;
            break;
        }
    }
    if( siz>1 && !~pos ) // can not determine , maybe multi guilty .
    {
        ans = -1;
        return;
    }
    if( siz==1 && !~pos )
        for(int i=1;i<=n;i++)
            if( !gul_can_be[i] )
            {
                may_be_ans[i] = 1;
                return;
            }
    for(int i=1;i<=n;i++)
        if( i!=pos && gul_can_be[i] == 1 ) // must be multi guilty in this statement
            return;
    may_be_ans[pos] = 1;
}
inline int count(int x)
{
    int ret=0;
    while(x)
        ret++,
        x -= (x&-x);
    return ret;
}
inline int getans()
{
    int ret = 0;
    for(int i=1;i<=n;i++)
        if( may_be_ans[i] )
        ret++,
        ans = i;
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    mx = (1<<n);
    for(int i=1;i<=n;i++)
    {
        cin>>name[i];
        person[name[i]] = person[name[i]+":"] = i;
    }
    char c = nextchar(1);
    while(c!='\n') c=nextchar();
    for(int i=1;i<=p;i++)
    {
        resbuf();
        getline();
        explain();
    }
    if( ans == -2 )
        return puts("Impossible"),0;
    for(int s=0;s<mx;s++)
    {
        if( count(s) != m ) continue;
        reslogic();
        int flag =1;
        for(int i=1;i<=n&&flag;i++)
            if( s & (1<<(i-1)) )
                logic(i,-1,flag);
            else logic(i,1,flag);
        if( flag && judgedate() )
            judgegul();
    }
    if( !~ans )
        return puts("Cannot Determine"),0;
    if( !getans() )
        return puts("Impossible"),0;
    if( getans() > 1 )
        return puts("Cannot Determine"),0;
    cout<<name[ans]<<endl;
    return 0;
}

2212: 合并果子(NOIP 2004 TGT2)

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1�−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。

输入

共两行。 第一行是一个整数 n(1≤n≤10000)�(1≤�≤10000) ,表示果子的种类数。 第二行包含 n� 个整数,用空格分隔,第 i� 个整数 ai(1≤ai≤20000)��(1≤��≤20000) 是第 i� 种果子的数目。

输出

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231231 。

样例输入 复制

3 
1 2 9

样例输出 复制

15

提示

对于 30%30% 的数据,保证有 n≤1000�≤1000:
对于 50%50% 的数据,保证有 n≤5000�≤5000;
对于全部的数据,保证有 n≤10000�≤10000。

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
int ans;
priority_queue <int,vector<int>,greater<int> >apple;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a;
        scanf("%d",&a);
        apple.push(a);
    }
    while(apple.size()>1)
    {
        int x=apple.top();apple.pop();
        int y=apple.top();apple.pop();
        apple.push(x+y);
        ans=ans+x+y;
    }
    printf("%d",ans);
    return 0;
}

2226: 过河(NOIP 2005 TGT2)

题目描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,⋯,L0,1,⋯,�(其中 L� 是桥的长度)。坐标为 00 的点表示桥的起点,坐标为 L� 的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是 S� 到 T� 之间的任意正整数(包括 S,T�,�)。当青蛙跳到或跳过坐标为 L� 的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度 L�,青蛙跳跃的距离范围 S,T�,�,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入

第一行有 11 个正整数 L(1≤L≤109)�(1≤�≤109),表示独木桥的长度。 第二行有 33 个正整数 S,T,M�,�,�,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,其中 1≤S≤T≤101≤�≤�≤10,1≤M≤1001≤�≤100。 第三行有 M� 个不同的正整数分别表示这 M� 个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

输出

一个整数,表示青蛙过河最少需要踩到的石子数。

样例输入 复制

10
2 3 5
2 3 5 6 7

样例输出 复制

2

提示

对于30%的数据,L≤104�≤104;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN=300 * 105;
ll read(){
    ll ans = 0, f = 1;
    char ch=getchar();
    while(!isdigit(ch))
    f*=(ch=='-')?-1:1,ch=getchar();
    do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
    while(isdigit(ch));
    return ans*f;
} 
ll L;
ll S,T,M;
int Nx[MAXN],a[MAXN];
int vis[MAXN],dp[MAXN];
int all;
int main(){
    L=read();
    S=read();
    T=read();
    M=read();
    int lcm=S*T;
    for(int i=1;i<=M;i++){
      Nx[i]=read(); 
    }
    sort(Nx+1,Nx+M+1);
    if(S==T){
        int cnt=0;
        for(int i=1;i<=M;i++)
        if(Nx[i]%S==0)all++;
        cout<<all<<endl;
        return 0; 
    }
    for(ll i=1;i<=M;i++){
    int len=Nx[i]-Nx[i-1];
    if(len>=lcm)len=lcm;
    a[i]=a[i-1]+len;
    vis[a[i]]=1;
    }
    L=a[M]+lcm;
    memset(dp,0x7f,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=L;i++)
    for(int j=S;j<=T;j++)
    {
        if(i-j>=0)if(vis[i])dp[i] = min(dp[i - j] + 1,dp[i]);
        else dp[i] = min(dp[i - j],dp[i]);
        }    
    int ans=0x7f;
    for(int i=a[M];i<=L;++i)ans=min(ans,dp[i]);
    cout << ans << endl;
    return 0;
}

2249: 金明的预算方案(NOIP 2006 TGT2)

题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 n� 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
 


如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 00 个、11 个或 22 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 n� 元。于是,他把每件物品规定了一个重要度,分为 55 等:用整数 1∼51∼5 表示,第 55 等最重要。他还从因特网上查到了每件物品的价格(都是 1010 元的整数倍)。他希望在不超过 n� 元的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j� 件物品的价格为 vj��,重要度为wj��,共选中了 k� 件物品,编号依次为 j1,j2,…,jk�1,�2,…,��,则所求的总和为:
vj1×wj1+vj2×wj2+⋯+vjk×wjk��1×��1+��2×��2+⋯+���×���。
请你帮助金明设计一个满足要求的购物单。

输入

第一行有两个整数,分别表示总钱数 n� 和希望购买的物品个数 m�。 第 22 到第 (m+1)(�+1) 行,每行三个整数,第 (i+1)(�+1) 行的整数 vi��,pi��,qi�� 分别表示第 i� 件物品的价格、重要度以及它对应的的主件。如果 qi=0��=0,表示该物品本身是主件。

输出

输出一行一个整数表示答案。

样例输入 复制

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

样例输出 复制

2200

提示

数据规模与约定

对于全部的测试点,保证 1≤n≤3.2×1041≤�≤3.2×104,1≤m≤601≤�≤60,0≤vi≤1040≤��≤104,1≤pi≤51≤��≤5,0≤qi≤m0≤��≤�,答案不超过 2×1052×105。

#include <stdio.h>
#include <stdlib.h>
#define maxn 32060

int N,m,v,p,q;
int main_item_value[maxn],main_item_weight[maxn];
int minor_item_value[maxn][3],minor_item_weight[maxn][3];
int f[maxn];

int max(int a,int b)
{
    return a>b?a:b;
}

int main()
{
    scanf("%d%d",&N,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&v,&p,&q);
        if(!q)
        {
            main_item_value[i]=v;
            main_item_weight[i]=v*p;
        }
        else
        {
            minor_item_value[q][0]++;
            minor_item_value[q][minor_item_value[q][0]]=v;
            minor_item_weight[q][minor_item_value[q][0]]=v*p;
        }
    }

    for(int i=1;i<=m;i++)
    {
        for(int j=N;j>=0;j--)
        {
            if(j>=main_item_value[i])
            {
                f[j]=max(f[j],f[j-main_item_value[i]]+main_item_weight[i]);
                if(j>=main_item_value[i]+minor_item_value[i][1])
                {
                    f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][1]]+main_item_weight[i]+minor_item_weight[i][1]);
                }
                if(j>=main_item_value[i]+minor_item_value[i][2])
                {
                    f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][2]]+main_item_weight[i]+minor_item_weight[i][2]);
                }
                if(j>=main_item_value[i]+minor_item_value[i][1]+minor_item_value[i][2])
                {
                    f[j]=max(f[j],f[j-main_item_value[i]-minor_item_value[i][2]-minor_item_value[i][1]]+main_item_weight[i]+minor_item_weight[i][2]+minor_item_weight[i][1]);
                }
            }
        }
    }
    printf("%d",f[N]);
    return 0;
}

2271: 字符串的展开(NOIP 2007 TGT2)

题目描述

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“`d-h`”或者“`4-8`”的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为“`defgh`”和“`45678`"。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:
(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“`-`”,减号两侧同为小写字母或同为数字,且按照`ASCII`码的顺序,减号右边的字符严格大于左边的字符。
(2) 参数p1�1:展开方式。p1=1�1=1时,对于字母子串,填充小写字母;p1=2�1=2时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3�1=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“\*”来填充。
(3) 参数p2�2:填充字符的重复个数。p2=k�2=�表示同一个字符要连续填充k个。例如,当p2=3�2=3时,子串“`d-h`”应扩展为“`deeefffgggh`”。减号两边的字符不变。
(4) 参数p3�3:是否改为逆序:p3=1�3=1表示维持原来顺序,p3=2�3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1=1�1=1、p2=2�2=2、p3=2�3=2时,子串“`d-h`”应扩展为“`dggffeeh`”。
(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“`d-e`”应输出为“`de`”,“`3-4`”应输出为“`34`”。如果减号右边的字符按照`ASCII`码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“`d-d`”应输出为“`d-d`”,“`3-1`”应输出为“`3-1`”。

输入

共两行。 第11行为用空格隔开的33个正整数,依次表示参数p1,p2,p3�1,�2,�3。 第22行为一行字符串,仅由数字、小写字母和减号“−−”组成。行首和行末均无空格。

输出

共一行,为展开后的字符串。

样例输入 复制

1 2 1
abcs-w1234-9s-4zz

样例输出 复制

abcsttuuvvw1234556677889s-4zz

提示

输入样例 #2

2 3 2
a-d-d

输出样例 #2

aCCCBBBd-d

40%40%的数据满足:字符串长度不超过55
100%100%的数据满足:1≤p1≤3,1≤p2≤8,1≤p3≤21≤�1≤3,1≤�2≤8,1≤�3≤2。字符串长度不超过100

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

inline bool isValid(char a,char b){
    if(a >= b) return false;
    if(isdigit(a) && isdigit(b)) return true;
    if((a >= 'a' && a <= 'z') && (b >= 'a' && b <= 'z')) return true;
    return false;
}

int main(){
//    freopen("expand.in", "r", stdin);
//    freopen("expand.out", "w", stdout);
    int p1 = read(), p2 = read(), p3 = read();
    string s;
    cin >> s;
    int len = s.length();
    for(int i=0; i<len; i++){
        if(s[i] == '-' && i != 0 && i != len-1 && isValid(s[i-1], s[i+1])){
            int add = (!isdigit(s[i-1]) && (p1 == 2)) * ('A' - 'a');
            char begin = s[i-1], end = s[i+1];
            if(p3 == 2) swap(begin, end);
            int d = (begin < end) ? 1 : -1;
            for(char k=begin; k!=end; k+=d){
                if(k == begin) continue;
                for(int j=1; j<=p2; j++){
                    if(p1 == 3) putchar('*');
                    else putchar(k+add);
                }
            } 
        }
        else putchar(s[i]);
    }
    return 0;
}

2275: 火柴棒等式(NOIP 2008 TGT2)

题目描述

给你n根火柴棍,你可以拼出多少个形如“A+B=C�+�=�”的等式?等式中的A�、B�、C�是用火柴棍拼出的整数(若该数非零,则最高位不能是00)。用火柴棍拼数字0−90−9的拼法如图所示:
 


注意:
1. 加号与等号各自需要两根火柴棍
2. 如果A≠B�≠�,则A+B=C�+�=�与B+A=C�+�=�视为不同的等式(A,B,C>=0�,�,�>=0)
3. n�根火柴棍必须全部用上

输入

一个整数n(n<=24)�(�<=24)。

输出

一个整数,能拼成的不同等式的数目。

样例输入 复制

14

样例输出 复制

2

提示

3>输入样例 #2

18

输出样例 #2

9

【输入输出样例1解释】
22个等式为0+1=10+1=1和1+0=11+0=1。
【输入输出样例2解释】
99个等式为:
0+4=4
0+11=11
1+10=11
2+2=4
2+7=9
4+0=4
7+2=9
10+1=11
11+0=11

#include<iostream>
 
using namespace std;
int n, sum;
int match[10] = { 6,2,5,5,4,5,6,3,7,6 };
 
int match_of(int n)
{
    int sum = 0, m = n;
    if (0 == n)
        return 6;
    else
    {
        for (; 0 != m; m /= 10)
            sum += match[m % 10];
        return sum;
    }
}
 
int main()
{
    cin >> n;
    if (n <= 10)
        cout << '0';
    else
    {
        n -= 4;
        for (int i = 0; i <= 1111; i++)
            for (int j = 0; j <= i; j++)
                if (n == match_of(j) + match_of(i - j) + match_of(i))
                    sum++;
        cout << sum;
    }
    return 0;
}

  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值