2017.8.15 考试

注:所有题目的时间限制均为 1s ,内存限制均为 256MB 。


1 1 .第K K 小数
( ( number .cpp/c/pas)
【问题描述】
有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数
相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
【输入格式】
输入文件名为number.in。
输入文件包含三行。
第一行为三个正整数N,M和K。
第二行为N个正整数,表示第一个数列。
第三行为M个正整数,表述第二个数列。
【输出格式】
输出文件名为number.out。
输出文件包含一行,一个正整数表示第K小数。
【输入输出样例1 1 】
number.in number.out
2 3 4
1 2
2 1 3
3
【输入输出样例2 2 】
number.in number.out
5 5 18
7 2 3 5 8
3 1 3 2 5
16
【数据规模与约定】
样例点编号 N M K 元素大小(≤)
1 20 20 150 10^4
2 50 50 2000 10^4
3 100 80 5000 10^9
4 200 200 26000 10^9
5 10000 10000 50050000 10^4
6 1000 20000 9500000 10^4
7 1000 20000 10000500 10^9
8 2000 20000 190000 10^9

9 2000 20000 199000 10^9
10 20000 20000 210005000 10^4
11 20000 20000 210000 10^5
12 20000 20000 200000 10^9
13 20000 20000 220000500 10^5
14 20000 20000 199000500 10^9
15 200000 200000 180000 10^4
16 200000 200000 200000 10^9
17 2000 200000 100001500 10^9
18 200000 180000 19550000000 10^5
19 200000 200000 19900010000 10^9
20 200000 200000 20000010000 10^9

二分答案

检验的时候,统计乘积<mid的数有多少个

枚举a数组,b数组如果二分是O(log*logn*logm) 会T 4个点

将a、b数组排序,那么随着a的递增,b中<mid的区间单调递减

这样总复杂度O(log(n+m))

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 200001
using namespace std;
typedef long long LL;
int n,m;
LL k;
int a[N],b[N];
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
void read(LL &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
bool check(LL mid)
{
    int last=m;
    LL sum1=0;
    for(int i=1;i<=n;++i)
    {
        while(last) 
        if(1ll*a[i]*b[last]>=mid) last--;
        else break;
        sum1+=last;
    }
    if(sum1<k) return true; 
    return false;
}
void out(LL x)
{
    if(x/10) out(x/10);
    putchar(x%10+'0');
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    read(n); read(m); read(k);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++) read(b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    LL l=1,r=1ll*a[n]*b[m],mid,ans;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    out(ans);
}
View Code

2 2 . dwarf tower
(dwarf.cpp/c/pas)
【问题描述】
Vasya在玩一个叫做"Dwarf Tower"的游戏,这个游戏中有n个不同的物品,
它们的编号为1到n。现在Vasya想得到编号为1的物品。
获得一个物品有两种方式:
1. 直接购买该物品,第i件物品花费的钱为ci
2. 用两件其他物品合成所需的物品,一共有m种合成方式。
请帮助Vasya用最少的钱获得编号为1的物品。
【输入格式】
第一行有两个整数n,m(1<=n<=10000,0<=m<=100000),分别表示有n种物品以
及m种合成方式。
接下来一行有n个整数,第i个整数ci表示第i个物品的购买价格,其中
0<=ci<=10^9。
接下来m行,每行3个整数ai,xi,yi,表示用物品xi和yi可以合成物品ai,其
中(1<=ai,xi,yi<=n; ai<>xi, xi<>yi, yi<>ai)
【输出格式】
一行,一个整数表示获取物品 1 的最少花费。
输入样例: 输出样例:
5 3
5 0 1 2 5
5 2 3
4 2 3
1 4 5
2
【数据规模与约定】
60%的数据,n<=100
100%的数据,n<=10000,m<=100000

x和y可以合成a,x向a连边,y向a连边

1——n 全部入队

spfa

#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
#define N 10001
int cost[N],tot,head[N];
bool vv[N];
struct node
{
    int to,nxt,with;
}e[200001];
queue<int>q;
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
void add(int t,int u,int v)
{
    e[++tot].to=t; e[tot].with=v; e[tot].nxt=head[u]; head[u]=tot;
    e[++tot].to=t; e[tot].with=u; e[tot].nxt=head[v]; head[v]=tot;
}
int main()
{
    freopen("dwarf.in","r",stdin);
    freopen("dwarf.out","w",stdout);
    int n,m,a,b,c;
    read(n); read(m);
    for(int i=1;i<=n;++i) read(cost[i]);
    for(int i=1;i<=m;i++) 
    {
        read(a),read(b),read(c);
        add(a,b,c);
    }
    for(int i=1;i<=n;i++) q.push(i);
    int now;
    while(!q.empty())
    {
        now=q.front(); q.pop(); vv[now]=false;
        for(int i=head[now];i;i=e[i].nxt)
         if(cost[e[i].to]>cost[now]+cost[e[i].with])
         {
             cost[e[i].to]=cost[now]+cost[e[i].with];
             if(!vv[e[i].to]) vv[e[i].to]=true,q.push(e[i].to);
         }
    }
    printf("%d",cost[1]);
}
View Code

3 . abcd
(abcd.cpp/c/pas)
【问题描述】
有4个长度为N的数组a,b,c,d。现在需要你选择N个数构成数组e,数组e满足
a[i]≤e[i]≤b[i]以及

N

Σ  e i ∗ c[i]  =0
i=1

并且使得

N

Σ  e i ∗ d[i]
i=1
最大。
【输入格式】
输入文件名为abcd.in。
输入文件共 N+1 行。
第 1 行包含1个正整数N。
第 i+1 行包含4个整数a[i],b[i],c[i],d[i]。
【输出格式】
输出文件名为abcd.out。
输出共1行,包含1个整数,表示所给出公式的最大值。输入数据保证一定有
解。
【输入输出样例1 1 】
abcd.in abcd.out
5
-1 1 2 5
-2 2 1 2
0 1 1 3
-2 -1 3 10
-2 2 3 9
2
【输入输出样例2 2 】
abcd.in abcd.out
10
1 10 1 7
-10 10 2 0
-10 10 2 2
-10 10 2 0
1 10 1 0
-10 10 2 0
90
10 10 2 0
1 10 1 0
-10 10 2 0
1 10 1 0
【输入输出样例3 3 】
abcd.in abcd.out
10
1 10 1 0
-10 10 2 2
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
1 10 1 0
-4
【数据规模与约定】
对于 20%的数据,N≤10,-2≤a[i]<b[i]≤2;
对于 60%的数据,N≤50, -20≤a[i]<b[i]≤20;
对于 100%的数据,
N≤200,-25≤a[i]<b[i]≤25,1≤c[i]≤20,0≤d[i] ≤10000

背包问题

c为体积,d为价值,每件物品有a——b件,选e件,填满一个体积为0的背包

两个问题:

1、a可能为负数

2、背包体积不能为0

所以

1、件数由a——b,转化为0——b-a,这样选e件相当于选e-a件

2、根据等式,左边是e件,实际选了e-a件,所以右边也要减去a*c,所以背包体积为 Σ-a[i]*c[i]

答案就是 Σ(e[i]-a[i])*d[i] + Σ a[i]*d[i]

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 201
using namespace std;
int a[N],b[N],c[N],d[N];
int num[N],v[N*N],w[N*N],f[N*500];
int tot,ans,n,m;
void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c)) { if(c=='-') f=-1; c=getchar();}
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}
void add(int k,int sum)
{
    v[++tot]=c[k]; w[tot]=d[k];
    int i=1;  sum--;
    while(sum-i*2>=0)
    {
        sum-=i*2;
        v[++tot]=i*2*c[k];
        w[tot]=i*2*d[k];
        i*=2;
    }
    if(sum) 
    {
        v[++tot]=sum*c[k];
        w[tot]=sum*d[k];
    }
}
int main()
{
    freopen("abcd.in","r",stdin);
    freopen("abcd.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
        num[i]=b[i]-a[i];
        ans+=a[i]*d[i];
        add(i,num[i]);
        m+=-a[i]*c[i];
    }
    memset(f,-0x3f3f3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=tot;i++)
     for(int j=m;j>=v[i];j--)
      f[j]=max(f[j],f[j-v[i]]+w[i]);
    printf("%d",f[m]+ans);
}
View Code
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7380314.html