2019.7.1 JZ DAY7总结

本文深入探讨了竞赛编程中的策略与技巧,通过具体题目解析,介绍了逆序对求解、最短路径算法应用以及字符串处理等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.前几天毕业典礼,所以没参加比赛。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值