2018GPLT
2018GPLT
7-1 天梯赛座位分配
一共有n所学校参加比赛,每所学校有\(a_i\)只队伍,每只队伍共10人,要保证每个学校的所有队员不能相邻就坐,令每一所学校的队伍排成一排纵列,然后从第一所学校开始到最后一个学校,每个学校的第一个人就坐,然后在第二个人,以此类推,如果最后只剩下一个学校,两两之间需要隔开
题解:模拟
直接看成以最多学校参赛人数为行,学校数为列的矩阵进行模拟即可,关键记录最大人数和次大人数即可
注意以下几点:
- 只有一个学校参加的情况
- 如果某个学校最后只有自己队伍了,那么我们需要注意还没到变成只有一个学校的时候最后安排座位的学校,如果该学校就是最后多余的学校,那么第一个多余的人的编号需要+2,否则只需要+1,然后后面一直+2即可
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e6 + 10, M = 4e5 + 10;
int n;
int a[N];
int ans[110][110];
void solve()
{
cin >> n;
int maxx[2] = {-INF, -INF};
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
a[i] *= 10;
if (maxx[0] < a[i])
{
maxx[1] = maxx[0];
maxx[0] = a[i];
}
else if (maxx[0] == a[i])
maxx[1] = a[i];
else if (maxx[1] < a[i])
maxx[1] = a[i];
}
int id = 0;
int pos = -1;
if (n == 1)
maxx[1] = maxx[0], id = -1;
for (int i = 1; i <= maxx[0]; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (i > a[j])
continue;
if (i == maxx[1])
pos = j;
if (i > maxx[1] && j == pos && pos != -1)
{
id += 2;
ans[i][j] = id;
pos = -1;
continue;
}
else if (i > maxx[1] && j != pos && pos != -1)
{
ans[i][j] = ++id;
pos = -1;
continue;
}
if (i > maxx[1] || n == 1)
{
id += 2;
ans[i][j] = id;
}
else
ans[i][j] = ++id;
}
}
for (int i = 1; i <= n; ++i)
{
cout << "#" << i << endl;
int ishead = 1;
for (int j = 1; j <= a[i]; ++j)
{
if (ishead == 0)
cout << " ";
ishead = 0;
cout << ans[j][i];
if (j % 10 == 0)
{
ishead = 1;
cout << endl;
}
}
}
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
7-2 倒数第N个字符串
给定一个完全由小写英文字母组成的字符串等差递增序列,该序列中的每个字符串的长度固定为 L,从 L 个 a 开始,以 1 为步长递增。例如当 L 为 3 时,序列为 { aaa, aab, aac, ..., aaz, aba, abb, ..., abz, ..., zzz }。这个序列的倒数第27个字符串就是 zyz。对于任意给定的 L,本题要求你给出对应序列倒数第 N 个字符串。
题解:思维
我们发现我们可以将每个字符串看成是26进制的数,a表示0,z表示25,那么\(zzz=z*26^2+z*26^1+z*26^0\),那么对于倒数第N个数,我们可以将其转化为正序的数,然后这是这个字符串10进制下的表达形式,那么直接十进制转26进制,倒序输出答案即可
注意:答案很有可能是aaaaa,那么其十进制的值为0,所以我们一定要用给定的位数L去计算答案,如果写成while (x),那么很有可能前面的a会被漏掉
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;
int n, x;
void solve()
{
cin >> n >> x;
int m = pow(26, n);
x = m - x;
vector<int> ans;
while (n--) //一定要用位数,如果写成while (x),那么很有可能前面的a会被漏掉
{
ans.push_back(x % 26);
x /= 26;
}
reverse(all(ans));
for (auto c : ans)
cout << (char)(c + 97);
cout << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
7-12 秀恩爱分得快
互联网上每天都有大量人发布大量照片,我们通过分析这些照片,可以分析人与人之间的亲密度。如果一张照片上出现了 K 个人,这些人两两间的亲密度就被定义为 1/K。任意两个人如果同时出现在若干张照片里,他们之间的亲密度就是所有这些同框照片对应的亲密度之和。下面给定一批照片,请你分析一对给定的情侣,看看他们分别有没有亲密度更高的异性朋友?
首先输出
A PA
,其中PA
是与A
最亲密的异性。如果PA
不唯一,则按他们编号的绝对值递增输出;然后类似地输出B PB
。但如果A
和B
正是彼此亲密度最高的一对,则只输出他们的编号,无论是否还有其他人并列。
题解:模拟
按照题意模拟即可,如果给定的情侣至少其中一个出现在照片中,那么我们就遍历这张照片,否则我们不去遍历,不然每张照片都看一遍会超时
注意以下几点:
- 也许没有照片
- 注意我们读入性别时需要用字符串,不然如果出现-0,我们会把他当成男性
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;
int n, m;
vector<int> p[1010];
double pa[N], pb[N];
double pa_max = 0, pb_max = 0;
int sex[N];
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; ++i)
{
int k;
cin >> k;
for (int j = 1; j <= k; ++j)
{
string s, s1;
cin >> s;
int x;
if (s[0] == '-')
{
s1 = s.substr(1);
x = stoi(s1);
}
else
x = stoi(s);
if (s[0] == '-')
sex[x] = 1;
else
sex[x] = 0;
p[i].push_back(x);
}
}
string s1, s2, s3;
cin >> s1 >> s2;
int a, b;
if (s1[0] == '-')
{
s3 = s1.substr(1);
a = stoi(s3);
}
else
a = stoi(s1);
if (s1[0] == '-')
sex[a] = 1;
else
sex[a] = 0;
if (s2[0] == '-')
{
s3 = s2.substr(1);
b = stoi(s3);
}
else
b = stoi(s2);
if (s2[0] == '-')
sex[b] = 1;
else
sex[b] = 0;
for (int i = 1; i <= m; ++i)
{
bool ina, inb;
if (find(all(p[i]), a) != p[i].end())
ina = true;
else
ina = false;
if (find(all(p[i]), b) != p[i].end())
inb = true;
else
inb = false;
int sz = p[i].size();
if (ina)
{
for (auto x : p[i])
{
if (sex[a] != sex[x])
{
pa[x] += 1.0 / sz;
pa_max = max(pa_max, pa[x]);
}
}
}
if (inb)
{
for (auto x : p[i])
{
if (sex[b] != sex[x])
{
pb[x] += 1.0 / sz;
pb_max = max(pb_max, pb[x]);
}
}
}
}
if (pa[b] == pa_max && pb[a] == pb_max)
{
if (sex[a] == 1)
cout << "-";
cout << a << " ";
if (sex[b] == 1)
cout << "-";
cout << b << endl;
}
else
{
for (int i = 0; i <= n - 1; ++i)
{
if (i == a)
continue;
if (pa[i] == pa_max)
{
if (sex[a] == 1)
cout << "-";
cout << a << " ";
if (sex[i] == 1)
cout << "-";
cout << i << endl;
}
}
for (int i = 0; i <= n - 1; ++i)
{
if (i == b)
continue;
if (pb[i] == pb_max)
{
if (sex[b] == 1)
cout << "-";
cout << b << " ";
if (sex[i] == 1)
cout << "-";
cout << i << endl;
}
}
}
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
7-14 至多删三个字符
给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串?
题解:DP+去重 :需要复习
状态表示:\(f[i][j]\):在\([1,i]\)中删除\(j\)个字符
状态属性:数量
状态转移:
选择删除第\(i\)个字符,那么会在\([1,i-1]\)中删除\(j-1\)个字符:\(f[i][j] += f[i-1][j-1]\)
不选择删除第\(i\)个字符,那么会在\([1,i-1]\)中删除\(j\)个字符:\(f[i][j]+=f[i-1][j]\)
去重:举个例子:abcdad,\(f[6][2]\)由\(f[5][2]\)和\(f[5][1]\)转移过来,那么对于\(f[5][2]\)来说他可以选择 \(da\)删除,对于\(f[5][1]\)来说,他必须选择删除第6位,然后它可以选择\(a\),即删除了\(ad\),那儿很显然两者选择这么删除后剩余的字符串\(abc\)一样,也就是说\(abc\)字符串的方案重复了,多加了,那么我们如何去重?
对于第\(i\)个位置,我们从后往前遍历找到其第一个位置\(k\),使得\(s[i]==s[k]\),那么存在两种情况:
举个例子\(abcdabd,j=2,i=7,k=4\),在这种情况下,我们并不会造成重复,因为我们只有删除\(abd\),才会产生重复,也就是说\(j=3\)的情况下会产生重复,但是现在\(j=2\),而第i个位置到第k个位置之间有\(i-k=3\)个字符,也就是例子中的\(abd\), 那么如果说\(j<i=k\),是不会造成重复的
还是上面的例子,但是现在\(j=4 > i-k=3\),也就是说\(f[6][3]\)可以选择删除\(abd\),但是这样还需要删除一个,我们只需要在\([1,k-1]\)之间再删除一个字符;对于\(f[6][4]\)来说它可以选择删除\(dab\),同样只需要再\([1,k-1]\)中删除一个字符,那么\(f[3][1]\)的方案数被重复加了,所以我们减去即可,总结以下,也就是说:如果\(j>=i-k\),那么\(f[i][j]-=f[k-1][j-(i-k)]\)
那么我们对于每个位置\(i\),只需要找到其前面第一个相同字符就可以\(break\),那为什么不用再往前找呢?我们看个例子:\(dacdead\),我们在处理最后一个d的时候前面的d的位置上的方案数是不是已经去重过了,也就说都是正确的,所以不需要花时间再去对第一个d去重,因为在倒数第2个d已经去重过了
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e6 + 10, M = 4e5 + 10;
string s;
int n;
int f[N][4];
void solve()
{
cin >> s;
n = s.length();
s = " " + s;
f[0][0] = 1;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j <= 3; ++j)
{
f[i][j] = f[i - 1][j];
if (j - 1 >= 0)
f[i][j] += f[i - 1][j - 1];
for (int k = i - 1; k >= 1; --k)
{
if (s[k] == s[i])
{
if (i - k <= j)
f[i][j] -= f[k - 1][j - (i - k)];
break;
}
}
}
}
cout << f[n][3] + f[n][2] + f[n][1] + f[n][0] << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}
7-15 神坛
在古老的迈瑞城,巍然屹立着 n 块神石。长老们商议,选取 3 块神石围成一个神坛。因为神坛的能量强度与它的面积成反比,因此神坛的面积越小越好。特殊地,如果有两块神石坐标相同,或者三块神石共线,神坛的面积为
0.000
。n(3 ≤ n ≤ 5000)
题解:计算几何之极角排序+叉积计算三角形面积
显然如果暴力,复杂度是\(O(n^3)\),显然不行,所以对于这道题我们可以对每一个点的相邻向量进行极角排序,那么这样的话我们就我们遍历到的相邻向量之间所形成的三角形都是最小三角形,我们只需要在这些三角形里面取最小即可;那儿这样的话就避免计算一些一定不是答案的向量,时间复杂度为\(O(n^2logn)\)
对于极角排序,我们可以选择利用叉积大小对相邻向量按照顺时针排序,因为我们知道向量a×向量b,如果叉积为正,说明向量b在向量a的顺时针方向,叉积为负,说明在向量a的逆时针方向,叉积为0,说明两个向量共线
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 5e3 + 10, M = 4e5 + 10;
int n;
struct point
{
int x, y;
} p[N];
int cross(int x1, int y1, int x2, int y2) //叉积
{
return x1 * y2 - x2 * y1;
}
bool cmp(point a, point b) //按照叉积进行极角排序
{
if (cross(a.x, a.y, b.x, b.y) == 0)
return a.x < b.x;
return cross(a.x, a.y, b.x, b.y) > 0;
}
void solve()
{
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> p[i].x >> p[i].y;
int ans = INF;
for (int i = 1; i <= n; ++i)
{
int cnt = 1;
vector<point> a;
for (int j = 1; j <= n; ++j)
{
if (i == j)
continue;
a.push_back({p[j].x - p[i].x, p[j].y - p[i].y}); //注意排序一定得是向量
}
sort(all(a), cmp);
for (int j = 0; j < n - 2; ++j)
{
int x1 = a[j].x, y1 = a[j].y;
int x2 = a[j + 1].x, y2 = a[j + 1].y;
ans = min(ans, abs(x1 * y2 - x2 * y1));
}
}
cout << fixed << setprecision(3) << ans * 0.5 << endl;
}
signed main(void)
{
Zeoy;
int T = 1;
// cin >> T;
while (T--)
{
solve();
}
return 0;
}