JZOJ2017.08.06 B组

59 篇文章 0 订阅

T1

Description

FJ有一架用来称牛的体重的天平。与之配套的是N(1<=N<=40)个已知质量的砝码(所有砝码质量的数值都在31位二进制内)。每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(FJ不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当FJ把砝码放到她的蹄子底下,她就会尝试把砝码踢到FJ脸上)。天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于C(1<=C<2^30)时,天平就会被损坏。
砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。
FJ想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为C,他不能把所有砝码都放到天平上。
现在FJ告诉你每个砝码的质量,以及天平能承受的最大质量。你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

Input

第1行: 两个用空格隔开的正整数,N和C。
第2..N+1行: 每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。

Output

第1行: 一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。

思路:

DFS+前缀和。
先做前缀和。
倒着搜。
如果s[i]

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
long long ans=-1,s[1010],a[1010];
void dfs(int c,long long sum)
{
     if(sum>m) return;
     if(s[c-1]+sum<=m) { ans=max(ans,s[c-1]+sum); return; }
     ans=max(ans,sum);
     for(int i=1;i<c;i++)
     {
             sum+=a[i];
             dfs(i,sum);
             sum-=a[i];
     }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); s[i]=s[i-1]+a[i]; }
    dfs(n+1,0);
    printf("%lld\n",ans);
    return 0;
}

T2

Description

   我们的郭嘉大大经过一段时间发现了袁绍这个人干大事而惜身,见小利而忘义,又逢曹操在招兵买马,决定逃离袁绍去投曹操,而我们的曹操在第M天招募良材,我们的郭嘉大大既不能早去,也不能晚去,于是乎,他就趁着这一段时间到其他的城市游历一番,而每两个城市之间只能坐马车来往,由于我们的郭嘉大大很贪钱,他想用最少的费用,所以需要我们帮他求出这一个最小的费用。

Input

  第一行包含两个数n,m, 表示有n个城市,和m天后曹操招纳良材。城市一就是郭嘉所在的城市,城市n就是曹操处。接下来n * (n – 1)行描述马车乘坐表。   第2到第n行就是描述的城市1到2… n的马车乘坐表.   第n + 1到第2n-1行描述的城市2到城市1,3..n的马车乘坐表… … 对每一行,首先有一个数T,表示城市I到城市J的马车以T为周期,接下来有T个数,表示每天的马车的价格,如果价格为0则表示没有马车可坐。(n <= 100, m <= 200, T <= 20, Price <= 50000)

Output

  如果存在这样的路线使郭嘉第m天到达曹操处,则输出最少的费用,否则输出0!

思路:

dp
设f[i][j]为第i天到达j市的最小费用
f[i,j]:=min(f[i,j],f[k,j-1]+a[k,i,j]);

代码:

uses math;
var a:array[0..200,0..200,0..200] of longint;
    f:array[0..200,0..200] of longint;
    n,m,i,j,k,x,t,l:longint;
procedure main;
begin
  fillchar(f,sizeof(f),$7f div 3);
  f[1,0]:=0;
  for j:=1 to m do for i:=1 to n do for k:=1 to n do
    if i<>k then f[i,j]:=min(f[i,j],f[k,j-1]+a[k,i,j]);
  if f[n,m]=maxlongint div 3 then write(0) else write(f[n,m]);
end;
procedure init;
begin
  read(n,m);
  for i:=1 to n do
  begin
    for j:=1 to n do
    begin
      if i=j then continue;
      read(t);
      for k:=1 to t do
      begin
        read(x);
        if x=0 then x:=maxlongint div 3;
        a[i,j,k]:=x;
      end;
      for k:=t+1 to m do
      begin
        l:=k mod t;
        if l=0 then l:=t;
        a[i,j,k]:=a[i,j,l];
      end;
    end;
  end;
end;
begin
  assign(input,'lines.in');
  assign(output,'lines.out');
  reset(input);
  rewrite(output);
  init;
  main;
  close(input);close(output);
end.

T3

Description

   小Y最近学得了最短路算法,一直想找个机会好好练习一下。话虽这么说,OJ上最短路的题目都被他刷光了。正巧他的好朋友小A正在研究一类奇怪的图,他也想凑上去求下它的最短路。

   小A研究的图可以这么看:在一个二维平面上有任意点(x,y)(0<=x<=N,0<=y<=M,且x,y均为整数),且(x,y)向(x-1,y)(必须满足1<=x)和(x,y-1)(必须满足1<=y)连一条边权为0的双向边。

   每个点都有一个非负点权,不妨设(x,y)的权值为F[x][y],则有:

   1.x=0或y=0:F[x][y]=1;2.其他情况:F[x][y]=F[x-1][y]+F[x][y-1]。

   现在,小Y想知道(0,0)到(N,M)的最短路,即使得经过的点的权值之和最小。为了炫耀自己学过最短路算法,他决定和你进行一场比赛,看谁的程序跑得快。然则小Y没有学过高精度算法,所以他希望输出答案时只输出答案模1000000007后的值。

Input

   一行两个正整数N,M,表示图的大小。

Output

   一行一个整数Ans,表示答案模1000000007后的值。

思路:

组合数+快速幂
保证n>m,其实这个矩阵的最小权值的路线就是(0,0)~(1,n)~(m,n)
那么答案就是n+1+f[m][1~n]
知道f[i][j]=C(j,j+i),C(m-1,n-1)+C(m,n)
答案就转移为C(n-m+1,m)+n

代码:

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
const long long mo=1000000007;
long long ans,n,m,t;
long long ksm(long long x,long long t)
{

    long long r=1;
    while (t)
    {

        if (t&1) r=(r*x)% mo;
        x=(x*x)%mo;
        t>>=1;

    }
    return r;

}
long long c(long long x,long long y)
{

    long long x1=1;
    for (long long i=x;i>x-y;--i) x1=(x1*i)% mo;
    for (long long i=m;i>0;--i) x1=(x1*ksm(i,mo-2))%mo;
    return x1;

}
int main()
{

    scanf("%lld%lld",&n,&m);
    if (n<m) 
    {

        t=n;n=m;m=t;

    }
    printf("%lld",(c(n+m+1,m)+n)% mo);
    return 0;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值