文章目录
【题库链接】
https://www.jisuanke.com/contest/7331?view=challenges
【A. Stoichiometry】
【题目大意】
化学方程式配平
【解题思路】
大模拟?不太想写
【AC代码】
https://blog.csdn.net/The___Flash/article/details/104598915
【B. Pokemon Go Go】
【题目大意】
有n个点,每个点有精灵,一个精灵可能有多个点,问你捕捉所有精灵走的最短路程是多少
【解题思路】
有两种方法,一种是记忆化搜索,一种是状压dp
记忆化搜索可以A,但是过不了这个数据
20
42 68 a
135 101 b
170 125 c
79 159 d
163 65 e
106 146 f
82 28 g
162 92 h
196 143 i
28 37 j
192 5 k
103 154 l
93 183 m
22 117 n
119 96 o
48 127 p
172 139 q
70 113 r
68 100 s
36 95 t
所以建议还是写状压dp,但是奇怪的是记忆化搜索在测试数据里面跑得比dp快???(黑人问号)
【AC代码】
//记忆化搜索
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int maxn = (1 << 22);
struct Node {
int x, y;
string s;
Node() { x = y = 0; }
Node(register int mx, register int my) {
x = mx, y = my;
}
}a[25];
inline int getdis(register Node& a, register Node& b) {
return abs(a.x - b.x) + abs(a.y - b.y);
}
int n, cnt;
bool vis[25];
int dis[25][25];
int dp[maxn][25];
map<string, int> mp;
inline int dfs(register int pos, register int num) {
if (!num) return dis[pos][0];
register int tmp = 0;
for (register int i = 1; i <= cnt; ++i) if (vis[i]) tmp |= (1 << i);
if (dp[tmp][pos]) return dp[tmp][pos];
register int res = INF;
for (register int i = 1; i <= n; ++i) {
if (vis[mp[a[i].s]]) continue;
vis[mp[a[i].s]] = true;
res = min(dfs(i, num - 1) + dis[pos][i], res);
vis[mp[a[i].s]] = false;
}
return dp[tmp][pos] = res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (register int i = 1; i <= n; ++i) {
cin >> a[i].x >> a[i].y >> a[i].s;
if (!mp[a[i].s]) mp[a[i].s] = ++cnt;
}
for (register int i = 0; i < n; ++i) {
for (register int j = i + 1; j <= n; ++j) {
dis[i][j] = dis[j][i] = getdis(a[i], a[j]);
}
}
cout << dfs(0, cnt) << endl;
return 0;
}
/*
状压dp
dp[i][j]表示在二进制下i情况到达点k的最短路程
参考自:https://blog.csdn.net/weixin_43014282/article/details/104582513
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
const int maxn = (1 << 22);
struct Node {
int x, y;
string s;
inline Node() { x = y = 0; }
inline Node(register int mx, register int my) {
x = mx, y = my;
}
}a[25];
inline int getdis(register Node& a, register Node& b) {
return abs(a.x - b.x) + abs(a.y - b.y);
}
vector<int> v[25];
map<string, int> mp;
int tot;
int dis[25][25];
int dp[maxn][25];
int ans = INF;
inline void dfs(register int cur, register int cnt) {
if (cnt == tot + 1) {
ans = min(ans, dp[cur][0]);
return;
}
register int l = v[cnt].size();
for (register int i = 0; i < l; ++i) {
dfs((cur | (1 << v[cnt][i])), cnt + 1);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
register int n;
cin >> n;
for (register int i = 1; i <= n; ++i) {
cin >> a[i].x >> a[i].y >> a[i].s;
if (!mp[a[i].s]) mp[a[i].s] = ++tot;
v[mp[a[i].s]].push_back(i);
}
for (register int i = 0; i < n; ++i) {
for (register int j = i + 1; j <= n; ++j) {
dis[i][j] = dis[j][i] = getdis(a[i], a[j]);
}
}
memset(dp, INF, sizeof(dp));
dp[0][0] = 0;
int num = 1 << (n + 1);
for (register int i = 0; i < num; ++i) {
for (register int j = 0; j <= n; ++j) {
if ((i & (1 << j)) == 0) {
for (register int k = 0; k <= n; ++k) {
dp[i | (1 << j)][j] = min(dp[i | (1 << j)][j], dp[i][k] + dis[k][j]);
}
}
}
}
dfs(1, 1);
cout << ans << endl;
return 0;
}
【C. Urban Design】
【题目大意】
有n条直线将平面分成多个区域,相邻区域名字不同,现在有m次询问,每次给你两个点问你这两点所在区域的名字是否相同
【解题思路】
很简单,就看这两点连线后与多少条直线相交,如果数量为奇数,那么显然名字不同,否则相同
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
struct pt {
ll x, y;
pt() {}
pt(ll xx, ll yy) {
x = xx, y = yy;
}
};
struct ln {
pt s, e;
ln() {}
}l[10010];
ll mul(ln a) {
pt s = a.s, e = a.e;
return e.x * s.y - s.x * e.y;
}
bool judge(ln a, pt b) {
pt s = a.s, e = a.e;
ll dx = e.x - s.x, dy = e.y - s.y;
return dy * b.x + mul(a) - dx * b.y < 0;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int s;
cin >> s;
for (int i = 1; i <= s; ++i) {
int a, b, c, d;
cin >> a >> b >> c >> d;
l[i].e = pt(a, b);
l[i].s = pt(c, d);
}
int T;
cin >> T;
while (T--) {
ll a, b, c, d;
cin >> a >> b >> c >> d;
ln tmp;
tmp.e = pt(a, b);
tmp.s = pt(c, d);
int sum = 0;
for (int i = 1; i <= s; ++i) {
if (judge(l[i], tmp.s) != judge(l[i], tmp.e)) ++sum;
}
if (sum & 1) cout << "different" << endl;
else cout << "same" << endl;
}
return 0;
}
【D. Smooth Array】
【题目大意】
给你一个长度为n的数组,每次你可以将数组中的一个数变成0 - s中的任意一个数,问你最少多少次后数组中所有连续的k个数的和为s
【解题思路】
二维dp,dp[i][j]表示前i个数和为j所需要的的最小次数,冉儿饿不会
【AC代码】
https://blog.csdn.net/tianyizhicheng/article/details/104600786
【E. Is-A? Has-A? Who Knowz-A?】
【题目大意】
有以下四种规则:
A is B, B is C => A is C
A has B, B is C => A has C
A is B, B has C => A has C
A has B, B has C => A has C
现在先告诉你n个已经成立的关系,然后让你推断另外m个关系是否正确
注意A is A成立,但是A has A不一定成立
当时WA就WA在这,比较可惜
【解题思路】
Floyd求传递闭包
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
bool has[510][510];
bool is[510][510];
map<string, int> mp;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
register int n, m;
cin >> n >> m;
memset(is, 0, sizeof(is));
memset(has, 0, sizeof(has));
int cnt = 0;
for (register int i = 1; i <= n; ++i) {
register string a, b, c;
cin >> a >> b >> c;
if (!mp.count(a)) mp[a] = ++cnt;
if (!mp.count(c)) mp[c] = ++cnt;
int x = mp[a], y = mp[c];
if (b[0] == 'h') {
has[x][y] = true;
}
else {
is[x][y] = true;
}
}
for (int k = 1; k <= cnt; ++k) {
for (int i = 1; i <= cnt; ++i) {
if (i == k) continue;
for (int j = 1; j <= cnt; ++j) {
if (j == k || i == j) continue;
if (is[i][k] && is[k][j]) is[i][j] = true;
}
}
}
for (int k = 1; k <= cnt; ++k) {
for (int i = 1; i <= cnt; ++i) {
if(i == k) continue;
for (int j = 1; j <= cnt; ++j) {
if ((has[i][k] && has[k][j]) || (has[i][k] && is[k][j]) || (is[i][k] && has[k][j])) has[i][j] = true;
}
}
}
for (int i = 0; i <= cnt; ++i) {
is[i][i] = true;
}
register int cas = 0;
while (m--) {
register string a, b, c;
cin >> a >> b >> c;
bool flag = false;
if (b[0] == 'h') flag = has[mp[a]][mp[c]];
else flag = is[mp[a]][mp[c]];
if (flag) printf("Query %d: true\n", ++cas);
else printf("Query %d: false\n", ++cas);
}
return 0;
}
【F. Atlantis】
【题目大意】
给你n个海上的点,每个点的访问时间为t,高度为h,没过1s海水会上涨1m,问你在所有点全部淹没前你能访问的最大数目是多少
【解题思路】
假设你的最优解为a1,a2,a3…am,那么其一定满足:
t(a1) <= h(a1)
t(a1) + t(a2) <= h(a2)
t(a1) + t(a2) + t(a3) <= h(a3)
…
所以我们首先需要对所有的点按高度排序,尽量将高度小的点优先访问,然后为了找到一组解,先将解队列置空,然后假设最优解里面存在ai,将ai入队,如果此时我们的总时间T > h(ai),那么我们将队列里面的访问时间最长的点出队,这样队列的最大长度即为最优解
事实上这个解法有点类似于单调队列,不同在于我们这里用到的是优先级队列
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
const int maxn = 200010;
struct Node {
int t, h;
inline bool operator < (const Node& m_n)const {
if(h == m_n.h) return t < m_n.t;
return h < m_n.h;
}
}a[maxn];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
register int n;
cin >> n;
for(register int i = 1; i <= n; ++i) {
cin >> a[i].t >> a[i].h;
}
sort(a + 1, a + n + 1);
register priority_queue<int> que;
register ll T = 0, ans = 0;
for(register int i = 1; i <= n; ++i) {
que.push(a[i].t);
T += a[i].t;
while(!que.empty() && T > a[i].h) {
T -= que.top();
que.pop();
}
ans = max((ll)que.size(), ans);
}
cout << ans << endl;
return 0;
}
【G. Sheba’s Amoebas】
【题目大意】
给你一个黑白图,问你独立黑块的个数
【解题思路】
签到题,简单dfs,每遇到一个黑块就把与他相连的黑块标记,计算总数目
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int n, m;
char Map[110][110];
bool vis[110][110];
int dx[] = { 0, 0, 1, -1, 1, -1, 1, -1 };
int dy[] = { 1, -1, 0, 0, 1, -1, -1, 1 };
bool flag = false;
inline void dfs(int x, int y, int startx, int starty) {
if (vis[x][y] && x == startx && y == starty) {
flag = true;
return;
}
if (vis[x][y]) return;
vis[x][y] = true;
bool flag = false;
for (int i = 0; i < 8; ++i) {
if (Map[x + dx[i]][y + dy[i]] == '#') {
dfs(x + dx[i], y + dy[i], startx, starty);
if (flag) return;
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
cin >> Map[i][j];
}
}
int sum = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (vis[i][j]) continue;
if (Map[i][j] == '#') {
flag = false;
dfs(i, j, i, j);
sum += flag;
}
}
}
cout << sum << endl;
return 0;
}
【H. Zebras and Ocelots】
【题目大意】
有一个由Z和O组成的柱子,每次最下面的O会变成Z并且该O以下的Z会变成O,问你几次之后全部都变成Z
【解题思路】
一开始看数据很小,以为暴力直接过,后来发现暴力的时间是指数级的,其实很简单,把Z看成1,把O看成0,那么每次操作相当于为二进制下加1,并且最后会变成1111111…,做个减法就好了
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
inline ll qpow(ll m, int n) {
ll res = 1;
while (n) {
if (n & 1) res *= m;
m *= m;
n >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
ll sum = 0;
for (int i = 1; i <= n; ++i) {
char c;
cin >> c;
if (c == 'Z') {
sum += qpow(2, n - i);
}
}
sum = qpow(2, n) - sum - 1;
cout << sum << endl;
return 0;
}
【I. Racing Around the Alphabet】
【题目大意】
有一个圆盘分成28份,每份都有字母,给你一句话,问你走完这句话要多长时间,圆盘直径60m,你的速度15m/s,每次要停顿1s
【解题思路】
纯模拟就行了,但是精度要求有点高
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
#define PI acos(-1)
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
map<char, int> mp;
for (int i = 0; i < 26; ++i) {
mp[i + 'A'] = i;
}
mp[' '] = 26, mp['\''] = 27;
int n;
cin >> n;
string s = "\n";
getline(cin, s);
while (n--) {
getline(cin, s);
double sum = 1.0;
int l = s.length();
for (int i = 1; i < l; ++i) {
int t = mp[s[i]], tt = mp[s[i - 1]];
int tmp = abs(mp[s[i]] - mp[s[i - 1]]);
sum += min(tmp, 28 - tmp) * PI / 7.0 + 1;
}
printf("%.10f\n", sum);
}
return 0;
}
【J. Lost Map】
【题目大意】
给出一个图的邻接矩阵,让你输出其最小生成树上的所有边
【解题思路】
MST裸题
【AC代码】
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int INF = 0x3f3f3f3f;
int G[2510][2510];
bool vis[2510];
int dis[2510];
int pre[2510];
vector<pair<int, int> > v;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> G[i][j];
}
}
for (int i = 1; i <= n; ++i) {
pre[i] = 1;
dis[i] = G[1][i];
}
vis[1] = true;
int sum = 0;
for (int i = 1; i < n; ++i) {
int k = -1;
int Min = INF;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[j] < Min) {
Min = dis[j];
k = j;
}
}
if (k == -1) break;
sum += Min;
vis[k] = true;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[j] > G[k][j]) {
dis[j] = G[k][j];
pre[j] = k;
}
if (pre[k] == j && G[k][j] == Min && Min != 0) {
v.push_back(make_pair(k, j));
}
}
}
for (auto& it : v) {
cout << it.first << " " << it.second << endl;
}
return 0;
}