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); }
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]); }
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); }