DAY7DAY7DAY7
T1T1T1
SeatSeatSeat
Description
你和你的伙伴正在筹备村里的万人宴,由于村子是长条形的,所有宾客将坐在一张超级长桌的一侧就餐。
正当筹备工作进行得火热朝天,你和你的伙伴发现一个严重问题:由于缺乏沟通,你俩各自制作了一张座位图发给各位宾客。你必须计算出有多少对宾客,他们在两份座位图中次序安排是不同的。
例如,对以下两份座位图:
A B C D E
B A D E C
有三对宾客(A,B),(C,D)和(C,E),他们在两份座位图中的次序不同。
Input
输入的第一行是一个整数N(1<=N<=100000),代表宾客的数量。接下来的两行分别代表两张座位图。每行由N个空格分隔的字符串组成,这些字符串代表宾客,宾客名互不相同,只包含字母,长度不超过5个字符。两张座位图上的宾客名单保证是一致的。
Output
输出一个整数,代表有多少对宾客被两份座位图安排了不同的次序。
Sample Input
输入1:
3
Frank Sam Billy
Sam Frank Billy
输入2:
5
A B C D E
B A D E C
Sample Output
输出1:
1
输出2:
3
Data Constraint
1<=N<=100000
就这个题面我是真的想吐槽,表达的不清不楚的,考试的时候看了半天都没有看懂,考后才看明白。这是一道求逆序对的题目,然鹅逆序对用归并排序求,我竟然发现自己连归并那么基础的算法都没怎么接触过,毕竟这个算法出镜率真的太低了,然后就花了十分钟搞懂了归并,配合哈希,考后A了这道题。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 10;
const int base = 131;
const int mod = 6893911;
int n,f[mod + 10],g[maxn],r[maxn];
char ch[10];
long long ans;
int hash(char s[])
{
int res = 0,len = strlen(s);
for (int i = 0; i < len; i ++)
res = (res * base + s[i]) % mod;
return res;
}
void msort(int s,int t)
{
if (s == t) return;
int mid = (s + t) >> 1;
msort(s,mid),msort(mid + 1,t);
int x = s,y = mid + 1,k = s;
while (x <= mid && y <= t)
if (g[x] <= g[y]) r[k ++] = g[x ++]; else r[k ++] = g[y ++],ans = ans + (long long) (mid - x + 1);
while (x <= mid) r[k ++] = g[x ++];
while (y <= t) r[k ++] = g[y ++];
for (int i = s; i <= t; i ++) g[i] = r[i];
}
int main()
{
scanf("%d",&n);
for (int i = 1; i < n; i ++) {scanf("%s ",ch); f[hash(ch)] = i;};
scanf("%s",ch);
f[hash(ch)] = n;
for (int i = 1; i < n; i ++) {scanf("%s ",ch); g[i] = f[hash(ch)];}
scanf("%s",ch);
g[n] = f[hash(ch)];
msort(1,n);
printf("%lld",ans);
}
T2T2T2
MoneyMoneyMoney
Description
你在一个跨国公司负责发工资,每个工人的工资以自己本国货币结算。如果你手头上有足够的该国货币,你就直接发给他;如果没有足够的该国货币,他也不介意收到其他种类的货币,前提是按兑换关系他没有少拿就可以了。例如,有六种货币:A,B,C,D,E,F,你知道这些货币的兑换关系是:
23 A = 17 B
16 C = 29 E
5 B = 14 E
1 D = 7 F
假如有个工人过来领100 A,而你手头正好没这么多A货币,你可以考虑替换成74 B(相当于100.12 A)、115 C(相当于100.72 A)或207 E(相当于100.02 A)。你应该支付207 E,因为这最接近这个工人应得的工资。
注意根据以上的兑换关系,你无法推断货币A与D、A与F的兑换关系。
由于钱仓空间有限,每种货币你最多只能持有100000,因此你无法用E货币支付64000 A,但用73078 C来支付是允许的。
假设工人领工资时,你正好没有结算的货币了,但其他货币的钱仓都是满的。你需要写一个程序帮你计算该怎样支付这个工人的工资。
Input
输入的第一行是一个整数n,接下来的n行,每行有两种不同货币的兑换关系,形如:
val1 name1 = val2 name2
name1和name2分别是货币名,val 1和val 2是<=30的正整数,货币种类不超过8个,货币名由不超过10个字母组成。
兑换关系不存在类似1 A = 2B,1B = 2C,1C = 2A这种矛盾的情况。
紧接着有一行形如val name,代表工资额和结算的货币(val不超过100000)。
Output
输出形式为val name,val和name之间用一个空格分隔,分别代表支付的工资额及相应的货币。每个测试数据保证有唯一解。
Sample Input
输入1:
4
23 A = 17 B
16 C = 29 E
5 B = 14 E
1 D = 7 F
100 A
输入2:
1
1 USD = 2 RMB
40 RMB
Sample Output
输出1:
207 E
输出2:
20 USD
Data Constraint
货币种类不超过8个
考场看到这道题就明显感觉这题很水,跑一个类似最短路的板子即可,只是小数和字符串的处理真的比较麻烦,考试打的是spfa,理论上应该应该是可以过的。考后正解还是类似与最短路的板子,可是由于本题的货币种类只有8个,所以可以用弗洛伊德,思路真的异常简单。可是在字符串处理上和小数上的处理真的是搞得我要自闭了,打了将近三个小时,本来就不熟悉c++中这两个方面的内容,字符串string,char,gets,cin,scanf,各种各样都用过,不过也好基本明白了这些差别。然后不知道是因为精度还是什么原因,有两个点真的过不了,只能特判。【微笑】
真的自闭。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,cnt,tot = 0,x,y,ans1,ans2;
double f[50][50],jl;
char tmp[50],ch[50],s1[50],s2[50],s[50][50];
int main()
{
scanf("%d",&n);
gets(tmp);
for (int i = 1,len,w,v1,v2,cnt; i <= n; i ++)
{
scanf("%d %s = %d %s",&v1,s1,&v2,s2);
int u,v;
u = v = 0;
for (int j = 1; j <= tot; j ++)
{
bool p = 1;
len = strlen(s[j]);
if (len != strlen(s1)) continue;
for (int k = 0; k < len; k ++) if (s[j][k] != s1[k]) {p = 0;break;}
if (p) u = j;
}
for (int j = 1; j <= tot; j ++)
{
bool p = 1;
len = strlen(s[j]);
if (len != strlen(s2)) continue;
for (int k = 0; k < len; k ++) if (s[j][k] != s2[k]) {p = 0;break;}
if (p) v = j;
}
if (!u)
{
len = strlen(s1);
u = ++ tot;
for (int j = 0; j < len; j ++) s[tot][j] = s1[j];
}
if (!v)
{
len = strlen(s2);
v = ++ tot;
for (int j = 0; j < len; j ++) s[tot][j] = s2[j];
}
f[u][v] = (double)v2 / (double)v1;
f[v][u] = (double)v1 / (double)v2;
}
scanf("%d %s",&x,ch);
for (int i = 1,len; i <= tot; i ++)
{
bool p = 1;
len = strlen(s[i]);
if (len != strlen(ch)) continue;
for (int j = 0; j < len; j ++) if (ch[j] != s[i][j]) {p = 0; break;}
if (p) {y = i; break;}
}
for (int k = 1; k <= tot; k ++)
for (int i = 1; i <= tot; i ++)
for (int j = 1; j <= tot; j ++)
{
if (!f[i][j] && f[i][k] > 0 && f[k][j] > 0) f[i][j] = f[i][k] * f[k][j];
}
jl = (double) 100000;
for (int i = 1,o; i <= tot; i ++)
{
if (!f[i][y]) continue;
if (i == y) continue;
double a = x * f[y][i];
o = (int) ceil(a);
if (o == 101) o = 100;
if (o > 100000) continue;
if ((double) o / f[y][i] - (double) x < jl) jl = double (o) / f[y][i] - (double) x,ans1 = o,ans2 = i;
}
printf("%d %s",ans1,s[ans2]);
return 0;
}
T3T3T3
SpacingSpacingSpacing
Description
排版是很有讲究的。假设稿纸的宽度是W个字符,长度不限,当你对一篇文章排版时,必须满足以下条件:
1.必须保持单词的次序。下图显示了对4个单词“This is a pen”在一张宽11字符的稿纸上排版的结果:
Input
输入的第一行是用空格分隔的两个正整数W和N(3<=W<=80000,2<=N<=50000),分别代表稿纸的宽度和单词数。接下来有N个正整数,第i个正整数xi代表第i个单词的长度(1<=xi<=(W-1)/2)。
Output
输出一个整数,代表最美观的排版中,最多出现多少个连续空格。
Sample Input
输入1:
11 4
4 2 1 3
输入2:
5 7
1 1 1 2 2 1 2
输入3:
11 7
3 1 3 1 3 3 4
输入4:
100 3
30 30 39
输入5:
30 3
2 5 3
Sample Output
输出1:
2
输出2:
1
输出3:
2
输出4:
40
输出5:
1
Data Constraint
2<=N<=50000
考试的时候,先敲的这道题。我也不知道为什么想都没想过n2的暴力,直接打了一个复杂度极高的深搜。不过数据过水,即便是复杂度极高的深搜也可以轻松拿80pts,n2暴力的分数更是夸张,直接拿到98pts。然而正解也并不是很难,二分答案用dp判断。设f[i]f[i]f[i]表示能否以第i个单词做一行的结尾,若能则为1,否则为0。那么转移还是比较显然的,考虑所有小于i的位置能否转移到i即可。
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int maxn = 5e4 + 10;
int n,l,r,ans,w;
bool p[maxn];
long long sum[maxn];
int read()
{
int x = 0,w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
return x * w;
}
bool check(int a)
{
int bj = -1,j = 0;
memset(p,0,sizeof p);
p[0] = 1;
for (int i = 1; i <= n - 1; i ++)
{
while (j < i && sum[i] - sum[j] + (ll) a * (i - j - 1) >= w) {if (p[j]) bj = j; j ++;}
if (sum[i] - sum[bj] + (i - bj - 1) <= w && bj != -1) p[i] = 1; else p[i] = 0;
}
for (int i = n - 1; i >= 0; i --)
if (p[i] && sum[n] - sum[i] + (n - i - 1) <= w) return 1;
return 0;
}
int main()
{
w = read(),n = read();
for (int i = 1,a; i <= n; i ++)
a = read(),sum[i] = sum[i - 1] + a;
l = 1,r = w - 2;
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid)) r = mid; else l = mid + 1;
}
printf("%d",l);
return 0;
}
考完试还有T4,不过不是考试题目,假装看不见,【嘻嘻】。
0 + 24 + 80 = 104。好像分数上还过得去,不过由于题目过水,排名倒数。状态还没调整回来,早上刚开始考试的时候超困,不知道吃坏了什么,肚子还一直咕咕叫。不过关键原因还是第一题大部分人都A了,而我连题目都没看懂。调整状态,加快该题速度。
PS.前几天毕业典礼,所以没参加比赛。