南邮第八届程序设计竞赛之网络预选赛小结 一(错题集)
帅神妹纸的生日礼物
比赛描述
A协大牛帅神的妹纸最近又要过生日了,然而妹纸心里早已有一个想要的礼物,妹纸想要的礼物就是10种稀有的钻石,帅神为了满足妹纸的要求,四处打探知道了钻石在一个神秘的地方,这个地方有很多神,所有神灵站成一排,每个神手上有一种钻石,然而懒虫帅神为了少走路,想着尽可能少的访问相邻神,从他们手中获得钻石,已知妹纸对于每种钻石需求的数量多少,求问帅神需要最少访问多少连续的神才可以获得妹纸需要钻石。
输入
本题为多组样例,第一行输入一个整数t(t <= 100),表示样例个数,对于每组样例,
第一行为一个由0-9数字组成的字符串组成(长度小于100,000),表示站成一排的神灵,每种数字代表该神拥有几号钻石
第二行为10个数字,分别表示妹纸需要的0-9号钻石是多少
输出
每组样例输出一个数字,表示最少访问多少个连续的神可以满足妹纸需求,如果无法满足,输出"Let's break up"。
样例输入
2
1234567890
0 0 0 1 1 0 1 0 0 0
1234567890
2 0 0 0 0 0 0 0 0 0
样例输出
4
Let's break up
题目分析:典型的两点法,用st和ed维护一段区间,先找第一段符合条件的,然后缩小范围判断,不满足了再往后扩大范围
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 100005;
int const INF = 0x3fffffff;
int need[15], has[15];
char s[MAX];
bool judge()
{
for(int i = 0; i < 10; i++)
if(has[i] < need[i])
return false;
return true;
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
memset(has, 0, sizeof(has));
scanf("%s", s);
int len = strlen(s);
for(int i = 0; i < 10; i++)
scanf("%d", &need[i]);
int st = 0, ed = 0, ans = INF;
while(ed < len)
{
while(!judge() && ed < len)
{
has[s[ed] - '0'] ++;
ed ++;
}
while(judge() && st < ed)
{
ans = min(ans, ed - st);
has[s[st] - '0'] --;
st ++;
}
}
if(ans == INF)
printf("Let's break up\n");
else
printf("%d\n", ans);
}
}
小明的排队
时间限制(普通/Java):2000MS/6000MS 运行内存限制:65536KByte
比赛描述
TC是个土豪,经常给整个集训队发苹果,并且喜欢将大家排成一队根据每个人的苹果数量做出一些奇怪的事情,比如让每个人算出自己右边苹果数量比自己少的人的个数。拿别人的手短,大家对于TC的奇怪嗜好也只能牵就了。
输入
输入第一行为一个T(T <= 6)表示数据组数,每组数据包含整数n(n个人, 1<=n<=100000),以及n个整数(正整数,代表每个人相应的苹果数量)
输出
对于每组数据输出n个整数,第i个整数表示第i个人右边苹果比他的少的人的个数。
样例输入
1
4 5 2 6 1
样例输出
2 1 1 0
提示
输入请用scanf,输出请用printf
输出数据的最后一个数字后面也有空格
题目分析:典型的逆序数问题,归并排序和树状数组都可以做
#include <cstdio>
#include <cstring>
int const MAX = 1e5 + 5;
int const INF = 0x7FFFFFFF;
int n, a[MAX], res[MAX];
struct DATA
{
int val, idx;
}d[MAX], a1[MAX], a2[MAX];
void merge_sort(int l, int mid, int r, DATA *d)
{
int len1 = mid - l;
int len2 = r - mid;
for(int i = 0; i < len1; i++)
a1[i] = d[i + l];
a1[len1].val = INF;
for(int i = 0; i < len2; i++)
a2[i] = d[i + mid];
a2[len2].val = INF;
int i = 0, j = 0, k = l, tmp = 0;
while(i < len1 || j < len2)
{
if(a1[i].val <= a2[j].val)
{
res[a1[i].idx] += tmp;
d[k ++] = a1[i ++];
}
else
{
if(a1[i].val != INF)
tmp ++;
d[k ++] = a2[j ++];
}
}
}
void Merge(int l, int r, DATA *d)
{
if(l == r - 1)
return;
int mid = (l + r) >> 1;
Merge(l, mid, d);
Merge(mid, r, d);
merge_sort(l, mid, r, d);
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
memset(res, 0, sizeof(res));
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
for(int i = 0; i < n; i++)
{
d[i].val = a[i];
d[i].idx = i;
}
Merge(0, n, d);
for(int i = 0; i < n; i++)
printf("%d ", res[i]);
printf("\n");
}
}
Money
比赛描述
土豪BJ当前身上有现金x元,为了接济他的穷diao基友Tc,他对Tc说你现在可以交换我当前现金的任意两个相邻数字最多k次,多出来的钱都给你,Tc想知道自己最多能得到多少现金。
输入
输入第一行为一个整数T代表数据组数,每组数据只有一行包括两个数字x和k,数字均不含前导零
(1 <= T <= 15, 1 <= x <= 1019, 0 <= k <= 100)
输出
对每组数据,输出一个数字表示Tc能得到的最多现金数。
样例输入
3
1399 3
256 1
109009 4
样例输出
7920
270
801891
题目分析:尽量让高位的数字大,从最高位开始向低位找,先找到k步之内的最大数,如果k步之内的最大数不是当前位置的数,那么把最大数交换到当前为止,然后减去初始值即可,因为没有负数,可以直接I64u
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ull unsigned long long
using namespace std;
char s[20];
int k;
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%s %d", s, &k);
ull bj_cash, tc_cash;
sscanf(s, "%I64u", &bj_cash);
int len = strlen(s), now;
for(int i = 0; i < len; i++)
{
now = i;
for(int j = i + 1; j <= i + k && j < len; j++)
if(s[j] > s[now])
now = j;
if(now != i)
{
for(int j = now; j > i; j--)
{
swap(s[j], s[j - 1]);
k --;
}
}
}
sscanf(s, "%I64u", &tc_cash);
printf("%I64u\n", tc_cash - bj_cash);
}
}