暑假习题 四

巧克力
巧克力
[Description]
yaoyiyao 带来了一块N*M*K 的巧克力,但是这一天机房的人很多,为了公平,tsyao 提议
先分成1*1*1 的,然后每人那一块,剩下的给yaoyiyao(毕竟是他的嘛)
但是yaoyiyao 不想把力气花在这上面,于是就交给了FinalSix 神牛,FinalSix 说我们应该用
最小的代价来划分这个巧克力。
有两种划分方法:
① 用手,每次只能掰一块,至于从哪里掰随便
② 用刀,每次可以将几块巧克力重起来,然后从某个位置一刀切下来
FinalSix 不想动手算了,想知道分别使用这两种方法的最少次数分别是多少
[Input]
一行三个整数N,M,K,之间用一个空格隔开
[Output]
两行,每行一个整数,分别表示两种方法的最少次数。

分析:
巧克力
第一问应该很好得知答案应该是N*M*K–1
首先我们在N 那一边切 N-1 刀变成N 块 1*M*K 的
然后每块1*M*K 的我们切M-1 刀,共 N×(M-1) 刀变成N*M 块1*1*K 的
然后每块再切K-1 刀即全部变成1*1*1,共 N*M*(K-1) 刀
最后加起来化简 N*M*K-1 刀
第二问因为可以重在一起切,所以我们每次尽可能地在中间切肯定是最优方案!然后切下来的两块是可以重在一起看成一块来处理的,所以切下来的一块是可以不管了的。最终的解法就是每次/2 就ok 了。

#include <cstdio>

long long n,m,k;

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    printf("%I64d\n",(long long)n*m*k-1);
    long long ans=0;
    while(n>1)
    {
        ans++;
        if(n%2)n=n/2+1;
        else n=n/2;
    }
    while(m>1)
    {
        ans++;
        if(m%2)m=m/2+1;
        else m=m/2;
    }
    while(k>1)
    {
        ans++;
        if(k%2)k=k/2+1;
        else k=k/2;
    }
    printf("%I64d",ans);
    return 0;
}

序列
[Description]
有一天OJ 给Jiangzh 了一个序列,但是OJ 给的任务没有省选operation 那么变态,OJ 只需
要Jiangzh 做两件事:
① 求出这个序列的最长上升子序列长度
② 这个序列中满足 i < j < k Ai < Aj > Ak 关系的序列个数
[Input]
第一行一个整数N,表示这个序列有N 个整数
第二行N 个整数Ai
[Output]
两行,每行一个整数分别为两个问题的答案
[Sample 1]
sequence.in
5
1 2 3 4 5
sequence.out
5
0
样例解释:最长上升子序列长度为5,满足条件②的序列有0 个

分析:

第一问:LIS
第二问:求序列个数
O(N^3)算法:枚举i,j,k,判断
O(N^2)算法:枚举j 作为中点,然后向左扫描得到比Aj 小的数的个数L,向右扫描
得到比Aj 小的数的个数R,然后利用乘法原理得到以j 作为中点的序列有L*R 个,把所
有的L*R 加起来即可
O(Nlog2 N)算法:和上面一样,只是找L 和R 的时候利用线段树。

#include<cstdio>
#include<cstring>
const int N=200000+10;
const int maxp=200000;
int n,a[N];
int val[N*4];
int l[N],r[N];
int c[N];

int LIS()
{
    int len=0;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=len,m;
        while(l<=r)
        {
            m=(l+r)>>1;
            if(a[i]>c[m]) l=m+1;
            else r=m-1;
        }
        c[l]=a[i];
        if(l>len) len=l;
    }
    return len;
}

void change(int p,int l,int r,int a)
{
    if(l==r && l==a)
    {
        val[p]++;//节点p包含数的个数增加1
        return;
    }
    int m=(l+r)>>1;
    if(a<=m) change(p<<1,l,m,a);
    if(a>m) change((p<<1)+1,m+1,r,a);
    val[p]=val[p<<1]+val[(p<<1)+1];
}
int query(int p,int l,int r,int a,int b)//查询线段树中[a,b]区间内比某数小的数的个数
{
    if(a<=l && b>=r) return val[p];
    int m=(l+r)>>1,x1=0,x2=0;
    if(a<=m) x1=query(p<<1,l,m,a,b);
    if(b>m) x2=query((p<<1)+1,m+1,r,a,b);
    return x1+x2;
}

long long solve()
{
    for(int i=1;i<=n;i++)
    {
        l[i]=query(1,0,maxp,0,a[i]-1);
        change(1,0,maxp,a[i]);
    }
    memset(val,0,sizeof(val));
    for(int i=n;i>=1;i--)
    {
        r[i]=query(1,0,maxp,0,a[i]-1);
        change(1,0,maxp,a[i]);
    }
    long long res=0;
    for(int i=1;i<=n;i++) res+=(long long)l[i]*r[i];
    return res;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    printf("%d\n",LIS());
    printf("%I64d\n",solve());
    return 0;
}

给力否?
[Description]
还记得Day1 的时候yuyanggo 去偷玉米吗?他最后被农场主逮了……
yuyanggo 想把玉米丢了,但是他的狗始终不干,没办法,只好带着玉米跑了~
由于玉米很重,逃跑的时候会消耗yuyanggo 的体力值,每跑1m 就会消耗一个体力值,所
以yuyanggo 必须选择一条最近的路(或者说是最后剩余体力值最多的路)跑回来。
有N 个点,1 为玉米地,N 为机房。
由于地形特殊,会有下坡路(用距离为负数表示),如-z,如果|z|<=N*10 那么,
yuyanggo 就不需要消耗体力,但是如果 |z|>N*10 就会因为坡太陡了不能走。
上面的规则仅在下坡路适用,其他的路每走1m 就消耗一个体力值。
情况紧急,yuyanggo 拿出了无线通话器,寻求信奥班的帮忙,告诉他怎么走最近。
[Input]
第1 行,一个整数P,表示yuyanggo 一开始的体力值
第2 行,两个整数,N 个点和M 条边
第3 ~ M+2 行,每行三个整数,x y z,表示x 到y 有一条长度为z(单位:m)的边,保证
x 到y 只会存在一条边。每两个整数用一个空格隔开.
[Output]
如果不能在体力耗尽之前跑回来或者没有完整的路跑回来,就输出“bu gei li a.”
如果能跑回来,就输出三行,第一行一个整数为剩余体力值,第二行一个整数为走过的点
数N(包含起终点),第三行N 个整数,即走过的路径,相邻整数用“->”(红色加粗部
分,不含引号,两个字符均为英文字符)分割。最后可能有多种方案,任意输出一种即可。
geili.in
10
4 6
1 2 5
1 3 3
2 3 5
3 2 2
3 4 7
2 4 5
geili.out
0
3
1->3->4

分析:
最短路的变形。
建图,用链表,注意是x 到y 的单向边。遇到下坡,判断一下,|z|<=N*10 就直接连称边权为0,|z|>N*10 就不连边,表示不能走。
然后就是最短路了。
用一个pre[]来记录路径就行了。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=0x3f3f3f;
int n,m,po;
int head[200005];
int vis[200005];
int dis[200005];
int p[200005];

struct edge{
  int v,w,next;
}e[200005];
int ans[200005];
int k=1;

void adde(int u,int v,int w)
{
  e[k].v=v;
  e[k].w=w;
  e[k].next=head[u];
  head[u]=k++;
}

void readdata(){
  memset(head,-1,sizeof(head));
  memset(dis,inf,sizeof(dis));//将所有dis初始化为inf 
  scanf("%d",&po);
  scanf("%d %d",&n,&m);
  int a,b,c;
  for(int i=1; i<=m; i++)
  { 
    scanf("%d%d%d",&a,&b,&c);
    if(c<0)
        {
            c=-c;
            if(c<=10*n) c=0;
            else continue;
        }
    adde(a,b,c); 
  }
}
int l=0;
void dijkstra()
{
     typedef pair<int,int>pii;
     priority_queue<pii,vector<pii>,greater<pii> >q;
     dis[1]=0;
     q.push(make_pair(dis[1],1));
     while(!q.empty())
     {
          int u=q.top().second;
          q.pop();
          if(vis[u])continue;
          vis[u]=1;
          for(int i=head[u];i!=-1;i=e[i].next)
          {
                  int v=e[i].v;
                  if(dis[v]>dis[u]+e[i].w)
                  {       p[v]=u;
                          dis[v]=dis[u]+e[i].w;
                          q.push(make_pair(dis[v],v));
                  }
          }
     }
}



int main()
{
readdata();
dijkstra();
int pas=1;
for(int i=1;i<=m;i++)
{
 if(p[i]>0 && p[i]!=p[i-1])pas++;
}
if(dis[n]>po)printf("bu gei li a.");
else 
{printf("%d\n",po-dis[n]);

int res,i=n;
ans[res=1]=n;

do{
        i=p[i];
        ans[++res]=i;
    }while(i!=1);
    printf("%d\n",res);
    for(int i=res;i>1;i--)printf("%d->",ans[i]);
    printf("%d\n",ans[1]);
return 0;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值