并查集板子
(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
18(多项式) 28/34(大模拟)
#include
#include
#include
using namespace std ;
const int N = 510 ;
int n, m, s, d, dist[N], g[N][N], num[N], r[N], rescure[N], pre[N] ;
// num表示到当前结点的路径数量
// r表示到当前结点的救援队数量 rescue表示当前结点的救援队数量
// pre表示走到当前结点的上一个结点
bool st[N] ;
void print_path(int i) // 递归打印
{
if (i == s)
{
printf("%d", i) ;
return ;
}
print_path(pre[i]) ;
printf(" %d", i) ;
}
void dijkstra()
{
memset(dist, 0x3f, sizeof dist) ;
dist[s] = 0 ;
num[s] = 1 ;
r[s] = rescure[s] ;
for (int i = 0;i < n;i ++ )
{
int t = -1 ;
for (int j = 0;j < n;j ++ )
{
if (!st[j] && (t == -1 || dist[t] > dist[j]))
{
t = j ;
}
}
st[t] = true ;
for (int j = 0;j < n;j ++ )
{
if (dist[j] > dist[t] + g[t][j])
{
dist[j] = dist[t] + g[t][j] ;
num[j] = num[t] ;
r[j] = r[t] + rescure[j] ;
pre[j] = t ;
}
else if (dist[j] == dist[t] + g[t][j])
{
num[j] = num[t] + num[j] ;
if (r[j] < r[t] + rescure[j])
{
r[j] = r[t] + rescure[j] ;
pre[j] = t ;
}
}
}
}
}
int main()
{
cin >> n >> m >> s >> d ;
for (int i = 0;i < n;i ++ ) cin >> rescure[i] ;
memset(g, 0x3f, sizeof g) ;
for (int i = 0;i < m;i ++ )
{
int a, b, c; cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
dijkstra() ;
printf("%d %d\n", num[d], r[d]);
print_path(d) ;
return 0 ;
}
思路: 用结构体数组存储这个链表,大小为maxn = 100000,node[i]表示地址为i的结点。在结构体中定义一个num变量,将num变量先初始化为2 * maxn。通过改变num变量的值最后sort排序来改变链表的顺序。
将没有删除的结点的num标记为cnt1,cnt1为当前没有删除的结点的个数;将需要删除的结点的num标记为maxn + cnt2,cnt2表示当前删除了的结点的个数,因为一开始初始化为了2 * maxn,所以我们可以通过对num排序达到:num = 0~maxn为不删除结点,num = maxn~2maxn为删除结点,num = 2maxn为无效结点
这样sort后就会按照需要输出的顺序将结点排序,我们只需要输出前cnt1+cnt2个结点即可~
#include
#include
#include
#include
#include
using namespace std;
const int N = 100010;
bool st[N];
struct node {
int addr, key, next, idx;
}nodes[N];
bool cmp(node a, node b) {
return a.idx < b.idx;
}
int main()
{
int h, n, x;
int cnt1 = 0, cnt2 = 0;
scanf("%d%d", &h, &n);
for (int i = 0; i < N; i ++ ) nodes[i].idx = 2 * N;
for (int i = 0; i < n; i ++ )
{
scanf("%d", &x);
nodes[x].addr = x;
scanf("%d%d", &nodes[x].key, &nodes[x].next);
}
for (int i = h; i != -1; i = nodes[i].next)
{
if (!st[abs(nodes[i].key)])
{
st[abs(nodes[i].key)] = true;
nodes[i].idx = cnt1 ++ ;
}
else
{
nodes[i].idx = N + cnt2;
cnt2 ++ ;
}
}
sort(nodes, nodes + N, cmp);
int cnt = cnt1 + cnt2;
for (int i = 0; i < cnt; i ++ )
{
if (i != cnt1 - 1 && i != cnt - 1)
{
printf("%05d %d %05d\n", nodes[i].addr, nodes[i].key, nodes[i+1].addr);
}
else
{
printf("%05d %d -1\n", nodes[i].addr, nodes[i].key);
}
}
return 0;
}
思路: 按权值进行从大到小排序, 优先选择权值大的数
#include
#include
#include
using namespace std;
const int N = 1010;
struct node
{
double num, price, weight;
}nodes[N];
bool cmp(node a, node b)
{
return a.weight > b.weight;
}
int main()
{
int n, need;
cin >> n >> need;
for (int i = 0; i < n; i ++ ) cin >> nodes[i].num;
for (int i = 0; i < n; i ++ ) cin >> nodes[i].price;
for (int i = 0; i < n; i ++ ) nodes[i].weight = nodes[i].price / nodes[i].num;
sort(nodes, nodes + n, cmp);
double res = 0;
for (int i = 0; i < n; i ++ )
{
if (nodes[i].num <= need)
{
res += nodes[i].price;
}
else
{
res += nodes[i].weight * need;
break;
}
need -= nodes[i].num;
}
printf("%.2lf\n", res);
return 0;
}
思路: 假设它是二叉搜索树,一开始isMirror为FALSE,根据二叉搜索树的性质将已知的前序转换为后序,转换过程中,如果发现最后输出的后序数组长度不为n,那就设isMirror为true,然后清空后序数组,重新再转换一次(根据镜面二叉搜索树的性质),如果依旧转换后数组大小不等于n,就输出no否则输出yes
#include
#include
#include
using namespace std;
const int N = 1010;
bool isMirror;
int pre[N], post[N], idx;
// get BST's sequence of post-order
void getpost(int root, int tail)
{
if (root > tail) return;
int i = root + 1, j = tail;
if (!isMirror)
{
// 找到右子树的根节点
while (i <= tail && pre[root] > pre[i]) i ++ ;
// 找到左子树的最后一个结点
while (j > root && pre[root] <= pre[j]) j -- ;
}
else
{
// 镜像
// 找到右子树的根节点
while (i <= tail && pre[root] <= pre[i]) i ++ ;
// 找到左子树的最后一个结点
while (j > root && pre[root] > pre[j]) j -- ;
}
// 判断是否到了根节点, 不判断也可以
// if (i - j != 1) return;
// 递归左子树
getpost(root + 1, j);
// 递归右子树
getpost(i, tail);
// 后序遍历结点
post[idx ++ ] = pre[root];
}
int main()
{
int n; cin >> n;
for (int i = 0; i < n; i ++ ) cin >> pre[i];
getpost(0, n - 1);
if (idx != n)
{
isMirror = true;
idx = 0;
getpost(0, n - 1);
}
if (idx == n)
{
// 注意输入格式, 不能有多余空格
cout << "YES" << endl << post[0];
for (int i = 1; i < idx; i ++ ) cout << " " << post[i];
}
else
{
cout << "NO" << endl;
}
return 0;
}
思路: 因为给出的集合里面含有重复的元素,而计算nc和nt不需要考虑两个集合里面是否分别有重复的元素,所以可以直接使用set存储每一个集合,然后把set放进一个数组里面存储。当需要计算集合a和集合b的相似度nc和nt的时候,遍历集合a中的每一个元素,寻找集合b中是否有该元素,如果有,说明是两个人公共的集合元素,则nc++,否则nt++(nt的初值为b集合里面本有的元素)
#include
#include
#include
using namespace std;
const int N = 100;
set<int> s[N];
int main()
{
int n, m, k, x, a, b;
cin >> n;
for (int i = 0; i < n; i ++ )
{
cin >> m;
for (int j = 0; j < m; j ++ )
{
cin >> x;
s[i].insert(x);
}
}
cin >> k;
for (int i = 0; i < k; i ++ )
{
cin >> a >> b;
int nc = 0, nt = s[b - 1].size();
for (auto it = s[a - 1].begin(); it != s[a - 1].end(); it ++ )
{
if (s[b - 1].find(*it) == s[b - 1].end())
nt ++ ;
else
nc ++ ;
}
double ans = (double)nc / nt * 100;
printf("%.2lf%%\n", ans);
}
return 0;
}
思路: 与后序中序转换为前序的代码相仿(无须构造二叉树再进行广度优先搜索~),只不过加一个变量index,表示当前的根结点在二叉树中所对应的下标(从0开始),所以进行一次输出先序的递归过程中,就可以把根结点下标index及所对应的值存储在map
level中,map是有序的会根据index从小到大自动排序,这样递归完成后level中的值就是层序遍历的顺序
#include
#include
#include
#include
using namespace std;
const int N = 50;
int n, post[N], in[N];
map<int, int> level;
void pre(int rt, int st, int ed, int idx)
{
if (st > ed) return;
int i = st;
while (i < ed && in[i] != post[rt]) i ++ ; // 找到当前子树根节点在中序遍历中的位置
// 先序遍历处理代码
level[idx] = post[rt];
pre(rt - 1 - ed + i, st, i - 1, 2 * idx + 1); // idx为子树的根结点在层序遍历中的位置
pre(rt - 1, i + 1, ed, 2 * idx + 2);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> post[i];
for (int i = 0; i < n; i ++ ) cin >> in[i];
pre(n-1, 0, n-1, 0);
auto it = level.begin();
printf("%d", it->second);
while (++it != level.end()) printf(" %d", it->second);
return 0;
}
思路: 用并查集。分别用两个结构体数组,一个data用来接收数据,接收的时候顺便实现了并查集的操作union,另一个数组ans用来输出最后的答案,因为要计算家庭人数,所以用visit标记所有出现过的结点,对于每个结点的父结点,people++统计人数。标记flag == true,计算true的个数cnt就可以知道一共有多少个家庭。排序后输出前cnt个就是所求答案, 合并的时候指向小的那个id, 保证以家庭为单位输出的id是此家庭最小的id
#include
#include
#include
using namespace std;
const int N = 1010;
struct DATA{
int id, fid, mid, num, area;
int cid[10];
} datas[N];
struct node{
int id, people;
double num, area;
bool flag = false;
}ans[N * 10];
int p[N*10];
bool vis[N*10];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void Union(int a, int b)
{
a = find(a);
b = find(b);
if (a > b) swap(a, b);
p[b] = p[a];
}
int cmp(node a, node b)
{
if (a.area != b.area)
return a.area > b.area;
else
return a.id < b.id;
}
int main()
{
int n, k, cnt = 0;
cin >> n;
for (int i = 0; i < N * 10; i ++ ) p[i] = i;
for (int i = 0; i < n; i ++ )
{
cin >> datas[i].id >> datas[i].fid >> datas[i].mid >> k;
vis[datas[i].id] = true;
if (datas[i].fid != -1) {
vis[datas[i].fid] = true;
Union(datas[i].id, datas[i].fid);
}
if (datas[i].mid != -1) {
vis[datas[i].mid] = true;
Union(datas[i].id, datas[i].mid);
}
for (int j = 0; j < k; j ++ )
{
cin >> datas[i].cid[j];
vis[datas[i].cid[j]] = true;
Union(datas[i].cid[j], datas[i].id);
}
cin >> datas[i].num >> datas[i].area;
}
for (int i = 0; i < n; i ++ )
{
int id = find(datas[i].id);
ans[id].id = id;
ans[id].num += datas[i].num;
ans[id].area += datas[i].area;
ans[id].flag = true;
}
for (int i = 0; i < N * 10; i ++ )
{
if (vis[i])
ans[find(i)].people ++ ;
if (ans[i].flag)
cnt ++ ;
}
for (int i = 0; i < N * 10; i ++ )
{
if (ans[i].flag)
{
ans[i].num = (double)(ans[i].num * 1.0 / ans[i].people);
ans[i].area = (double)(ans[i].area * 1.0 / ans[i].people);
}
}
sort(ans, ans + N * 10, cmp);
cout << cnt << endl;
for (int i = 0; i < cnt; i ++ )
printf("%04d %d %.3lf %.3lf\n", ans[i].id, ans[i].people, ans[i].num, ans[i].area);
return 0;
}
思路: 有两种可能,一种是回文字符串的长度为奇数,一种是偶数的情况。i为字符串当前字符的下标。
当回文字串为奇数的时候,j表示i-j与i+j构成的回文字串长度;当回文字串长度为偶数的时候,j表示i+1左边j个字符一直到i右边j个字符的回文字串长度~
用maxvalue保存遍历结果得到的最大值并且输出
#include
#include
#include
#include
using namespace std;
const int N = 1010;
int main()
{
string s;
getline(cin, s);
int res = -1, temp = 0;
for (int i = 0; i < s.size(); i ++ )
{
// 奇数情况
temp = 1;
for (int j = 1; j < s.size(); j ++ )
{
if (i - j < 0 || i + j >= s.size() || s[i - j] != s[i + j]) break;
temp += 2;
}
res = max(res, temp);
// 偶数情况
temp = 0;
for (int j = 1; j < s.size(); j ++ )
{
if (i - j + 1 < 0 || i + j >= s.size() || s[i - j + 1] != s[i + j]) break;
temp += 2;
}
res = max(res, temp);
}
cout << res << endl;
return 0;
}
思路: 用结构体存储下每一个结点的id、收入、收到红包的总数,然后进行排序后输出
#include
#include
#include
using namespace std;
const int N = 10010;
struct node {
int id, tot, num;
}nodes[N];
bool cmp(node a, node b)
{
if (a.tot != b.tot)
return a.tot > b.tot;
else if (a.num != b.num)
return a.num > b.num;
else
return a.id < b.id;
}
int main()
{
int n, k, id, val;
cin >> n;
for (int i = 1; i <= n; i ++ )
{
cin >> k;
nodes[i].id = i;
for (int j = 0; j < k; j ++ )
{
cin >> id >> val;
nodes[id].tot += val;
nodes[i].tot -= val;
nodes[id].num ++ ;
}
}
sort(nodes + 1, nodes + n + 1, cmp);
for (int i = 1; i <= n; i ++ )
{
double t = (double)(nodes[i].tot * 1.00 / 100);
printf("%d %.2lf\n", nodes[i].id, t);
}
return 0;
}
分析:朋友之间的关系用并查集解决,敌人之间的关系用enemy[a] [b] = enemy[b] [a] = 1解决,因为朋友之间的朋友也是朋友,是传递关系,而敌人的敌人不一定是敌人,所以只需要用一个二维数组即可标记。
#include
#include
#include
using namespace std;
const int N = 110;
int p[N], rival[N][N];
int find(int x)
{
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
void _union(int a, int b)
{
a = find(a);
b = find(b);
p[a] = p[b];
}
int main()
{
int n, m, k, a, b, c;
cin >> n >> m >> k;
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 0; i < m; i ++ )
{
cin >> a >> b >> c;
if (c == 1) _union(a, b);
else {
rival[a][b] = 1;
rival[b][a] = 1;
}
}
for (int i = 0; i < k; i ++ )
{
cin >> a >> b;
if (find(a) == find(b) && rival[a][b] == 0)
puts("No problem");
else if (find(a) == find(b) && rival[a][b] == 1)
puts("OK but...");
else if (find(a) != find(b) && rival[a][b] == 0)
puts("OK");
else if (find(a) != find(b) && rival[a][b] == 1)
puts("No way");
}
return 0;
}
思路: 与前序中序转换为后序的代码相仿(无须构造二叉树再进行广度优先搜索~),只不过加一个变量index,表示当前的根结点在二叉树中所对应的下标(从0开始),所以进行一次输出先序的递归的时候,就可以把根结点下标所对应的值存储在level数组中),镜面反转只需改变index的值,左孩子为2 * index + 2, 右孩子为2 * index + 1
#include
#include
#include
#include
using namespace std;
const int N = 50;
int pre[N], in[N];
map<int, int> lev;
void level(int rt, int st, int ed, int idx)
{
if (st > ed) return;
int i = st;
while (i < ed && in[i] != pre[rt]) i ++ ; // 确定当前子树的根节点
lev[idx] = pre[rt];
level(rt + 1, st, i - 1, idx * 2 + 2);
level(rt + i - st + 1, i + 1, ed, idx * 2 + 1);
}
int main()
{
int n; cin >> n;
for (int i = 0; i < n; i ++ ) cin >> in[i];
for (int i = 0; i < n; i ++ ) cin >> pre[i];
level(0, 0, n-1, 0);
auto it = lev.begin();
cout << it->second;
while (++it != lev.end())
{
printf(" %d", it->second);
}
return 0;
}
思路: 必须注意,因为题目要求按照插入的顺序建立,所以是边插入边调整的,必须用向上调整,每次输入一个数之后就将它向上调整。直接模拟
#include
#include
#include
#include
using namespace std;
const int N = 1010;
int n, m, h[N];
map<int, int> mp;
void up(int x)
{
while (x/2 && h[x/2] > h[x])
{
swap(h[x/2], h[x]);
x >>= 1;
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
{
cin >> h[i];
up(i);
}
for (int i = 1; i <= n; i ++ ) mp[h[i]] = i;
string s;
int x, y;
for (int i = 0; i < m; i ++ )
{
cin >> x >> s;
if (s[0] == 'a')
{
cin >> y;
getline(cin, s);
if (mp[x]/2 == mp[y]/2) cout << 'T' << endl;
else cout << 'F' << endl;
} else {
cin >> s;
cin >> s;
if (s[0] == 'r') {
if (mp[x] == 1) cout << 'T' << endl;
else cout << 'F' << endl;
} else if (s[0] == 'p') {
cin >> s;
cin >> y;
if (mp[y]/2 == mp[x]) cout << 'T' << endl;
else cout << 'F' << endl;
} else {
cin >> s;
cin >> y;
if (mp[x]/2 == mp[y]) cout << 'T' << endl;
else cout << 'F' << endl;
}
}
}
return 0;
}
思路: 用图的深度优先遍历判断一个图内的连通分量有多少个,标记为cnt,之后对于每一个输入数据,因为城市a被攻占,所以把a的所有路径标注为不可达(0),再统计连通分量的个数tempcnt,如果tempcnt > cnt + 1,也就是说当现在的连通分量多余以前的连通分量+1的时候,说明改变了图的连通性;(因为城市被攻占本身它城市自己就变成了一个单独的城市,多出来一个连通分量,只要tempcint <= cnt + 1都说明没有改变图的连通性),每一次tempcnt在用完之后把cnt的值更新为tempcnt,保证下一次的判断是建立再已经失去之前这么多城市的基础之上的。
因为题目中说输入保证给出的被攻占的城市编号都是合法的且无重复,所以如果城市失去了n个,就是当前输入的是从0开始的第n-1个数据的时候,就说明Game Over了,最后当if(i == n – 1) printf(“Game Over.\n”);
#include
#include
#include
using namespace std;
const int N = 510;
bool st[N];
int g[N][N], n, m, k;
void dfs(int x)
{
st[x] = true;
for (int i = 0; i < n; i ++ )
if (!st[i] && g[x][i] == 1)
dfs(i);
}
int calc_cnt()
{
int cnt = 0;
memset(st, false, sizeof st);
for (int i = 0; i < n; i ++ )
if (!st[i])
{
dfs(i);
cnt ++ ;
}
return cnt;
}
int main()
{
cin >> n >> m;
int a, b, c;
for (int i = 0; i < m; i ++ )
{
cin >> a >> b;
g[a][b] = g[b][a] = 1;
}
int cnt = calc_cnt();
cin >> k;
for (int i = 0; i < k; i ++ )
{
cin >> c;
for (int j = 0; j < n; j ++ )
{
g[c][j] = g[j][c] = 0;
}
int t_cnt = calc_cnt();
if (t_cnt > cnt + 1)
printf("Red Alert: City %d is lost!\n", c);
else
printf("City %d is lost.\n", c);
cnt = t_cnt;
if (i == n - 1)
printf("Game Over.\n");
}
return 0;
}
思路: **必须要车号大的先出,小的后出。所以列车排队的每一队必须是从大到小排列(从右往左看),才能保证开出去的车也是从大到小的。对于每一个想要进入并列铁轨的车,如果车号大于每一队的队尾的车号,说明不能进入已经有的队伍,必须进入新的铁轨,否则,选择一个最接近它车号的尾部车号的队伍进入。其实无需保存每一个并行队列的所有值,只需要保存当前队伍的车尾(就是每一列最左边 即 每一列的最小值)即可,因为每一次都是需要排序比较大小的,所以用set自动排序,首先把set里面放入一个0值。每一次set的最后一个值s.rbegin()都是当前所有队列队尾的最大值。如果当前想要进入排队队伍的t值比集合里面最大值小,就移除第一个比他大的值,然后把t插入集合中。表示的是将t值插入了最接近它车号的队伍的队尾,否则就直接插入进去t值。作为新的队伍。s.upper_bound(t)返回的是第一个大于t的迭代器的位置,在前面加星号表示取这个位置的值,所以s.erase(*(s.upper_bound(t)));表示删除当前这个刚好大于t的位置处的值。 ** 因为一开始插入了一个没有的0,所以最后输出是s.size()-1;
#include
#include
#include
#include
using namespace std;
const int N = 10010;
set<int> s;
int n, t;
int main()
{
cin >> n;
s.insert(0);
// set自动排序
for (int i = 0; i < n; i ++ )
{
cin >> t;
if (t < *s.rbegin()) // end指向最后一个元素的后一个, rbegin指向最后一个元素
{
s.erase(*(s.upper_bound(t)));
}
s.insert(t);
}
cout << s.size() - 1 << endl;
return 0;
}
直接模拟
#include
#include
#include
#include
using namespace std;
const int N = 10010;
double stu[N], grade[20];
int main()
{
int n, k, m; cin >> n >> k >> m;
for (int i = 0; i < n; i ++ )
{
double mmax = -1, mmin = 110, sum = 0;
for (int j = 0; j < k; j ++ )
{
cin >> grade[j];
sum += grade[j];
mmax = max(mmax, grade[j]);
mmin = min(mmin, grade[j]);
}
stu[i] = (sum - mmax - mmin) / (k - 2);
}
sort(stu, stu + n);
for (int i = n - m; i < n; i ++ )
{
if (i == n - 1) printf("%.3lf\n", stu[i]);
else printf("%.3lf ", stu[i]);
}
return 0;
}
思路: 用gender数组记录每个人的性别,例如 gender[i]=‘F’ 表示编号为 i 的人的性别为女,开始时我们就先判断他们是否为同性,如果不是同性再判断是否在五代之内,如何判断呢?设带判断的两个人的编号分别为a,b,那么可以先进行依次DFS将a本身及其以上五代内的人都进行标记,然后再对b进行同样的操作,如果在对b进行DFS的过程中,出现了标记过的人了,则表明不能结婚,否则能结婚。
#include
#include
#include
#include
using namespace std;
const int N = 100010;
int n, k;
char gender[N];
bool st[N], flag;
vector<int> g[N];
void dfs(int u, int d)
{
if (d == 5) return;
if (st[u])
{
flag = true;
return;
}
st[u] = true;
for (int i = 0; i < g[u].size(); i ++ )
dfs(g[u][i], d + 1);
}
int main()
{
int x, f, m;
cin >> n;
for (int i = 0; i < n; i ++ )
{
cin >> x;
cin >> gender[x] >> f >> m;
if (f != -1) gender[f] = 'M', g[x].push_back(f);
if (m != -1) gender[m] = 'F', g[x].push_back(m);
}
cin >> k;
int a, b;
for (int i = 0; i < k; i ++ )
{
cin >> a >> b;
if (gender[a] == gender[b])
{
cout << "Never Mind" << endl;
continue;
}
memset(st, false, sizeof st);
flag = false;
dfs(a, 0);
dfs(b, 0);
if (flag)
cout << "No" << endl;
else
cout << "Yes" << endl;
}
return 0;
}
思路: 直接模拟
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 100010;
LL p[N];
int main()
{
int n; cin >> n;
for (int i = 0; i < n; i ++ ) cin >> p[i];
sort(p, p + n);
LL res = 0;
for (int i = 0; i < n; i ++ )
{
if (i <= n / 2 - 1) res -= p[i];
else res += p[i];
}
cout << "Outgoing #: " << n / 2 + (n & 1) << endl ;
cout << "Introverted #: " << n / 2 << endl ;
cout << "Diff = " << res << endl ;
return 0;
}
思路:
思路: 将关注的人存储在集合set里,将点赞的人和点赞的次数存储在map中,并统计点赞的平均次数sum / M,遍历map,如果map的值大于平均次数,且在set中找不到该用户名,就输出当前用户名(因为map中的键是已经按照字典序排序过的,所以直接输出就可以),并用flag标记是否有过输出,如果从始至终没有输出,说明没有悄悄关注的人,就输出Bing Mei You
#include
#include
#include
#include
#include
using namespace std;
const int N = 10010;
map<string, bool> st;
map<string, int> mp;
int main()
{
int n, m, cnt, avg = 0;
string s;
cin >> n;
for (int i = 0; i < n; i ++ )
{
cin >> s;
st[s] = true;
}
cin >> m;
for (int i = 0; i < m; i ++ )
{
cin >> s >> cnt;
mp[s] = cnt;
avg += cnt;
}
avg /= m;
bool flag = false;
for (auto it : mp)
{
if (it.second > avg && !st[it.first])
{
cout << it.first << endl;
flag = true;
}
}
if (!flag) cout << "Bing Mei You" << endl;
return 0;
}
思路: 用二维数组v存储师门谱系关系,v[i]表示编号为i的师傅所拥有的徒弟,如果徒弟个数等于0,也就是说这是个得道者,那么v[i] [0]保存放大的倍数,而且用visit[i] = true标记当前的这个编号的人是得道者~用深度优先搜索,每当遇到visit[index] = true也就是说这是个得道者的时候,就累加放大后的功力,power * v[index] [0],累加到result中~遍历v[index]的所有弟子,并将功力减弱r%,也就是power * (1 – r/100),最后输出的是result的整数值(int)result
#include
#include
#include
#include
using namespace std;
const int N = 100010;
vector<vector<int> > v;
int n, k;
double z, r, res = 0;
bool st[N];
void dfs(int idx, double p)
{
if (st[idx])
{
res += p * v[idx][0];
return;
}
for (int i = 0; i < v[idx].size(); i ++ )
dfs(v[idx][i], p * (1 - r / 100));
}
int main()
{
cin >> n >> z >> r;
v.resize(n);
int t;
for (int i = 0; i < n; i ++ )
{
cin >> k;
if (k == 0)
{
cin >> t;
v[i].push_back(t);
st[i] = true;
}
for (int j = 0; j < k; j ++ )
{
cin >> t;
v[i].push_back(t);
}
}
dfs(0, z);
cout << (int)res << endl;
return 0;
}
思路: 用一个a数字标记某个标签是否访问过,统计每个人点赞的标签数,用部分排序或者优先队列,最后输出结果
// 按照 comp 排序规则,对 [first, last) 范围的数据进行筛选并排序
void partial_sort (RandomAccessIterator first,
RandomAccessIterator middle,
RandomAccessIterator last,
Compare comp);
partial_sort() 函数会以交换元素存储位置的方式实现部分排序的。具体来说,partial_sort() 会将 [first, last) 范围内最小(或最大)的 middle-first 个元素移动到 [first, middle) 区域中,并对这部分元素做升序(或降序)排序。
#include
#include
#include
#include
using namespace std;
const int N = 1e7 + 10;
bool st[N];
struct usr {
string name;
int sum, cnt;
};
bool cmp(usr a, usr b)
{
if (a.cnt != b.cnt) return a.cnt > b.cnt;
return 1.0 * a.sum / a.cnt < 1.0 * b.sum / b.cnt;
}
int main()
{
int n; cin >> n;
vector<usr> v(n);
for (int i = 0; i < n; i ++ )
{
memset(st, false, sizeof st);
int k, t, cnt = 0;
string name;
cin >> name >> k;
for (int j = 0; j < k; j ++ )
{
cin >> t;
if (!st[t])
{
st[t] = true;
cnt ++ ;
}
}
v[i] = {name, k, cnt};
}
int minv = min(n, 3);
partial_sort(v.begin(), v.begin() + minv, v.end(), cmp);
for (int i = 0; i < minv; i ++ )
{
if (i != 0) cout << " ";
cout << v[i].name;
}
for (int i = 0; i < 3 - minv; i ++ )
cout << " -";
cout << endl;
return 0;
}
思路: 用结构体{id, data, next}存储节点信息,模拟链表。先遍历一遍链表,把有用数节点存起来然后从后往前交替输出,(先把顺序存在ans中,待会统一输出),l, r代表将要输出的节点位置,当(l-1)-(r+1) == 1时都遍历一遍了,可退出循环
#include
#include
#include
#include
using namespace std;
const int N = 100010;
struct node {
int id, data, next;
}nodes[N];
int main()
{
int h, n;
cin >> h >> n;
vector<node> v, ans;
for (int i = 0; i < n; i ++ )
{
int tid, tdata, tnext;
cin >> tid >> tdata >> tnext;
nodes[tid] = {tid, tdata, tnext};
}
while (h != -1)
{
v.push_back(nodes[h]);
h = nodes[h].next;
}
int l = 0, r = v.size() - 1;
while (1)
{
ans.push_back(v[r]);
r -- ;
if (r - l == -1) break;
ans.push_back(v[l]);
l ++ ;
if (r - l == -1) break;
}
for (int i = 0; i < ans.size(); i ++ )
{
if (i != ans.size() - 1)
printf("%05d %d %05d\n", ans[i].id, ans[i].data, ans[i+1].id);
else
printf("%05d %d -1\n", ans[i].id, ans[i].data);
}
return 0;
}
思路: 给出无向图,和颜色的数量k,求是否给出的颜色数量==k,并且任意两个点之间颜色都不同。
依次判断每一个点是否和周围的点颜色相同,出现一次,就立刻得出结果“No”,如果一次都没出现,结果为“Yes”。
判断给出的颜色个数的时候用set方便(本题数据规模不大,不会对运行时间影响太大)
#include
#include
#include
#include
#include
using namespace std ;
const int N = 510 ; int n, m, k ;
vector<vector<int>> v(N) ; // 用 vector 作邻接表来存边
int color[N] ; // 记录 每个点的 color
bool check(int i) // 检查每个点的 color 是否与其邻边不一样
{
for (int j = 0;j < v[i].size();j ++ )
{
if (color[i] == color[v[i][j]]) return false ;
}
return true ;
}
int main()
{
cin >> n >> m >> k ;
for (int i = 0;i < m;i ++ )
{
int a, b; cin >> a >> b;
v[a].push_back(b) ;
v[b].push_back(a) ;
}
int t ; cin >> t ;
while (t -- )
{
bool flag = true ; set<int> s ; // set 存储当前图的颜色种数 去重
for (int i = 1;i <= n;i ++ )
{
cin >> color[i] ;
s.insert(color[i]) ;
}
if (s.size() != k)
{
cout << "No" << endl ;
flag = false ;
}
else
{
for (int i = 1;i <= n;i ++ )
{
if (!check(i))
{
cout << "No" << endl ;
flag = false ;
break ;
}
}
}
if (flag) cout << "Yes" << endl ;
}
return 0 ;
}
思路: 并查集
#include
#include
#include
using namespace std;
const int N = 10010;
int p[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n, k, t, x; cin >> n;
int mmax = -1;
for (int i = 1; i <= N - 10; i ++ ) p[i] = i;
for (int i = 0; i < n; i ++ )
{
cin >> k;
cin >> t;
mmax = max(mmax, t);
for (int j = 1; j <= k - 1; j ++ )
{
cin >> x;
mmax = max(mmax, x);
int a = find(x), b = find(t);
if (a != b) p[a] = p[b];
}
}
int cnt = 0;
for (int i = 1; i <= mmax; i ++ )
if (i == find(i)) cnt ++ ;
cout << mmax << " " << cnt << endl;
int q; cin >> q;
while (q -- )
{
int a, b; cin >> a >> b;
if (find(a) != find(b)) cout << "N" << endl;
else cout << "Y" << endl;
}
return 0;
}
思路: 存图用邻接表存,aa数组表示此城有兄弟城市(即有多少城市与之连接),a组为每次攻城后的次城有多少兄弟城,aa作为原始连接情况,不变,每次用的时候,复制到a上即可。每攻下一座城,次城兄弟成变成0,其兄弟城的兄弟城减少一个。最后检查,如果所有城市的兄弟城都小于零,则输出YES,否则输出NO。
#include
#include
#include
#include
using namespace std;
const int N = 10010;
int n, m, k, aa[N], a[N];
void check()
{
for (int i = 1; i <= n; i ++ )
{
if (a[i] > 0)
{
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
return;
}
int main()
{
cin >> n >> m;
vector<vector<int> > v(n + 1);
while (m -- )
{
int c1, c2;
cin >> c1 >> c2;
aa[c1] ++ ;
aa[c2] ++ ;
v[c1].push_back(c2);
v[c2].push_back(c1);
}
cin >> k;
while (k -- )
{
int cnt, num;
cin >> cnt;
for (int i = 1; i <= n; i ++ ) a[i] = aa[i];
for (int i = 0; i < cnt; i ++ )
{
cin >> num;
a[num] = 0;
for (int j = 0; j < v[num].size(); j ++ )
a[v[num][j]] -- ;
}
check();
}
return 0;
}
思路: 广度优先搜索最长路径问题(深搜可能会超时),老祖宗的父母即root的id用0表示 ,队列中的结构体成员分别是,人的编号和所处层数 – 处理递增
#include
#include
#include
using namespace std;
const int N = 100010;
vector<int>v[N];
int n ,a[N] ,depth[N] ,flag;
int bfs() {
queue<int>q;
q.push(v[0][0]);//根节点只有一个孩子,直接把这个孩子传入队列
int x;
while (!q.empty()) {
x = q.front();
q.pop();
for (int i = 0; i < v[x].size(); i++) {//搜索所有的孩子
depth[v[x][i]] = depth[x] + 1;//深度为父节点+1
q.push(v[x][i]);
}
}
return depth[x];//返回树的深度
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] == -1) {//将根节点改为0
a[i] = 0;
depth[i] = 1;//根节点的深度为1
}
v[a[i]].push_back(i);//i是a[i]的孩子
}
int ans = bfs();
cout << ans << endl;
// 按顺序遍历
for (int i = 1; i <= n; i++) {//遍历找出距离根节点最远的叶节点
if (depth[i] == ans) {
if(flag) cout << ' ';
cout << i ,flag = 1;
}
}
cout << endl;
return 0;
}
思路: 给每个大于60分的学生发20块,如果还有人同时大于G,在发30块。把所有学生生排序后,重新排名。第一个学生为第一名,以后的学生如果分数和前一个学生并列,他的名次和前一个同学名次一样,否则他的名次为下标加一
#include
#include
#include
#include
using namespace std;
const int N = 10010;
struct stu {
string name;
int score;
};
int rk[N];
bool cmp(stu a, stu b)
{
if (a.score != b.score) return a.score > b.score;
return a.name < b.name;
}
int main()
{
int n, g, k, sum = 0;
cin >> n >> g >> k;
vector<stu> v(n);
for (int i = 0; i < n; i ++ )
{
cin >> v[i].name >> v[i].score;
if (v[i].score >= 60) sum += 20;
if (v[i].score >= g) sum += 30;
}
sort(v.begin(), v.end(), cmp);
rk[0] = 1;
for (int i = 1; i < n; i ++ )
{
if (v[i].score == v[i-1].score)
rk[i] = rk[i-1];
else
rk[i] = i + 1;
}
cout << sum << endl;
for (int i = 0; rk[i] <= k && i < n; i ++ )
cout << rk[i] << " " << v[i].name << " " << v[i].score << endl;
return 0;
}
思路:
- 此题考查的是对 0 -0 的特殊处理。当遇到 0 时候把此人转为1000存储, 所以读数据的时候要以字符串形式读取
- 遍历每张照片,把与男主女主对应的异性亲密度用sum数字累加起来,
并维护maxn[1] maxn[2], 为男主女主的最亲密值 和ans[1], ans[2]容器,为最亲密异性id。- 判断男主女主是否互为最亲密,如果是,输出并return 0, 否则分别输出他们的最亲密好友
- 注意输出时候,因为把0当1000存储,会导致0号人排在最后,
这是不符题意的,输出之前排个序,让1000排在最前面
#include
#include
#include
#include
using namespace std;
bool cmp (int a, int b) {
if(abs(a) == 1000) return true;
if(abs(b) == 1000) return false;
return abs(a) < abs(b);
}
int main(){
int n, m, num, k, sex[1010] = {0}, love[3] ;
double sum[1010] = {0}, maxn[3] = {0} ;
string s ;
cin >> n >> m ;
vector<vector<int>> v(m), ans(3);
for(int i = 0; i < m; i++) {
cin >> k;
for(int j = 0; j < k; j++){
cin >> s;
if(s == "0") s = "1000";
if(s == "-0") s = "-1000";
num = stoi(s); // 字符串转换函数
sex[abs(num)] = num;
v[i].push_back(num);
}
}
for(int i = 1; i <= 2; i++) {
cin >> s;
if(s == "0") s = "1000";
if(s == "-0") s = "-1000";
love[i] = stoi(s);
}
for(int k = 1; k <= 2; k++) {
for(int i = 0; i < m; i++) {
int flag = 0;
for(int j = 0; j < v[i].size(); j++){
if(v[i][j] == love[k]) {
flag = 1;
break;
}
}
if(flag == 1) {
for(int j = 0; j < v[i].size(); j++){
if(love[k] * v[i][j] < 0) {
sum[(int)abs(v[i][j])] += 1.0 / v[i].size();
}
}
}
}
}
maxn[1] = maxn[2] = -1;
for(int k = 1; k <= 2; k++) {
for(int i = 1; i <= 1000; i++) {
if(love[k] * sex[i] < 0) {
if(sum[i] > maxn[k]) {
maxn[k] = sum[i];
ans[k].clear();
ans[k].push_back(sex[i]);
}else if(sum[i] == maxn[k]) {
ans[k].push_back(sex[i]);
}
}
}
}
if(maxn[1] == sum[(int)abs(love[2])] && maxn[2] == sum[(int)abs(love[1])]) {
string s1 = to_string(love[1]), s2 = to_string(love[2]);
if(love[1] == 1000) s1 = "0";
if(love[1] == -1000) s1 = "-0";
if(love[2] == 1000) s2 = "0";
if(love[2] == -1000) s2 = "-0";
cout << s1 << " " << s2 << endl;
return 0;
}
for(int k = 1; k <= 2; k++) {
sort(ans[k].begin(), ans[k].end(), cmp);
for(int i = 0; i < ans[k].size(); i++) {
string s1 = to_string(love[k]), s2 = to_string(ans[k][i]);
if(love[k] == 1000) s1 = "0";
if(love[k] == -1000) s1 = "-0";
if(ans[k][i] == 1000) s2 = "0";
if(ans[k][i] == -1000) s2 = "-0";
cout << s1 << " " << s2 << endl;
}
}
return 0;
}
思路: 1、判断一个数是不是特立独行的幸福数,同时累计其独立性。2、判断其是不是素数,是的话独立性需要乘二。用num[i]储存第i个元素的独立性,用notIndep[i]表示第i各数不是特立独行的幸福数——将所有迭代产生的数标记为1,ans用来储存可能的幸福数,mark用来存储一个数迭代过程中产生的数,如果重复则说明不是幸福数
#include
using namespace std;
int A, B, flag, num[10001], notIndep[10001];
bool isPrime(int a) {
if (a == 1) return false;
for (int i = 2; i <= sqrt(a); i++)
if (a % i == 0) return false;
return true;
}
bool isIndep(int x) {
set<int> mark;
int X = x, temp1, temp2;
while (X != 1) {
mark.insert(X);
temp1 = 0;
while (X) {
temp2 = X % 10;
X /= 10;
temp1 += temp2 * temp2;
}
num[x]++;
notIndep[temp1] = 1;
X = temp1;
if (mark.count(X)) return false;
}
return true;
}
int main() {
cin >> A >> B;
vector<int> ans;
for (int i = A; i <= B; i++)
if (isIndep(i)) ans.push_back(i);
for (int i = 0; i < ans.size(); i++) {
if (isPrime(ans[i])) num[ans[i]] <<= 1;
if (!notIndep[ans[i]]) {
cout << ans[i] << ' ' << num[ans[i]] << endl;
flag = 1;
}
}
if (!flag) cout << "SAD";
return 0;
}
思路: 通过判断姓氏末位的字母来判断性别以及是否是维京人,如果是维京人还需要切割尾缀并将父亲姓名存储,之后再查询的时候,进入check函数内,枚举五代内的关系,判断是否有公共祖先~Record[A]用来存储A的性别以及父亲姓名,最后按题意输出
#include
#include
#include
#include
using namespace std;
typedef pair<int, string> PIS;
const int N = 100010;
map<string, PIS> record; // 映射关系: 孩子的名 -> 父亲的名
string check(string s1, string s2)
{
int cnt1 = 0, cnt2 = 0;
while (s1 != "")
{
cnt2 = 0;
string s3 = s2;
while (s3 != "")
{
if (s3 == s1 && (cnt1 < 4 || cnt2 < 4)) return "No";
if (cnt1 >= 4 && cnt2 >= 4) return "Yes";
s3 = record[s3].second;
cnt2 ++ ;
}
s1 = record[s1].second;
cnt1 ++ ;
}
return "Yes";
}
int main()
{
int n, m; cin >> n;
string fName, lName;
for (int i = 0; i < n; i ++ )
{
cin >> fName >> lName;
if (lName.back() == 'n') record[fName] = {1, lName.substr(0, lName.length()-4) };
else if (lName.back() == 'r') record[fName] = {0, lName.substr(0, lName.length()-7) };
else if (lName.back() == 'm') record[fName].first = 1;
else record[fName].first = 0;
}
cin >> m;
string fName1, fName2, tmp;
for (int i = 0; i < m; i ++ )
{
cin >> fName1 >> tmp >> fName2 >> tmp;
if (!record.count(fName1) || !record.count(fName2)) cout << "NA\n";
else if (record[fName1].first == record[fName2].first) cout << "Whatever\n";
else cout << check(fName1, fName2) << endl;
}
return 0;
}
思路: 用Edge储存编号为i的门可以通向的门,因为门可以从这里走过去,也可以从那里走过来,所以不要忘记从可以通向的那个门朝第i号门建边。vis[i]表示第i个门是否访问过,使用BFS从1号门出发走一遍,用one来保存队列中门的编号,根据结果唯一以及BFS的特性,最后一个one即是最远的那个门
#include
using namespace std;
#define pii pair<int, int>
int N, K, D, one, vis[100001];
vector<int> Edge[100001];
queue<int> Q;
int main() {
cin >> N;
for (int i = 1; i <= N; ++i) {
cin >> K;
while(K--) {
cin >> D;
Edge[D].push_back(i);
Edge[i].push_back(D);
}
}
Q.push(1);
vis[1] = 1;
while(!Q.empty()) {
one = Q.front();
Q.pop();
for (auto v:Edge[one]) {
if (vis[v]) continue;
vis[v] = 1;
Q.push(v);
}
}
cout << one;
return 0;
}
思路: cnt表示当前所需要装填箱子的颜色编号,这里使用了一个名叫stk的数字来模拟堆栈操作,用idx来表示堆顶位置。在模拟过程中,如果当前输入等于需要填装的编号,那么需要进一步判断堆顶元素是否是下一个需要的编号。只有在堆容量内才会继续向stk压入新元素,最后判断idx是否回归到初始值,则可判断整体是否可以愉快地完工
#include
#include
#include
using namespace std;
const int N = 1010;
int stk[N];
int main()
{
int n, m, k; cin >> n >> m >> k;
for (int i = 0; i < k; i ++ )
{
bool flag = true;
int idx = -1, cnt = 1, x;
for (int j = 0; j < n; j ++ )
{
cin >> stk[++ idx];
while (idx >= 0 && stk[idx] == cnt) idx --, cnt ++ ;
if (idx + 1 > m)
{
flag = false;
// break;
}
}
if (idx == -1 && flag) puts("YES");
else puts("NO");
}
return 0;
}
/*
注意! 在这里不能直接break! 在这里我们是在输入时进行判断,即使你输入一部分就能判断NO,后面的数据依然要输入,等输入完才能进行判断,如果你提前终止,那么留在缓冲区的数据会影响会留给后面的cin接收,所以一定要等输入结束才能进行判断!
*/
思路: 直接用数组模拟, 存储的时候倒序存储, 计算的时候顺序计算, 即可模拟栈的效果
#include
#include
#include
using namespace std;
const int N = 1010;
int n, in[N], res;
char op, record[N];
int main()
{
cin >> n;
for (int i = n - 1; i >= 0; i -- ) cin >> in[i];
for (int i = n - 1; i > 0; i -- ) cin >> record[i];
res = in[0];
for (int i = 1; i <= n - 1; i ++ )
{
op = record[i];
if (op == '+') res = in[i] + res;
else if (op == '-') res = in[i] - res;
else if (op == '*') res = in[i] * res;
else
{
if (res == 0)
{
printf("ERROR: %d/0\n", in[i]);
return 0;
}
res = in[i] / res;
}
}
cout << res << endl;
return 0;
}
思路: 首先用结构体node作为输入的申请信息的基本结构,将输入的申请信息记录在Record中,同时用j标注这是今天的第几次申请。isIllegal函数验证其身份证的合法性,如果不合法就把它的名字改成空用以标记非法用户。lastGet中储存上次领到口罩的时间。ill记录合法的生过病的人,gotten记录是否已经添加到合法生病名单中。最后检查所有合法用户,并检查距离上一次领口罩是否间隔P天,后完成口罩发放
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 10010;
struct node {
string name, id, time;
int isIll, order;
};
bool isLegal(const string &s)
{
if (s.size() != 18) return false;
for (auto it : s) if (!isdigit(it)) return false;
return true;
}
// 容器结构体排序用const
bool cmp(const node &a, const node &b)
{
if (a.time != b.time) return a.time < b.time;
return a.order < b.order;
}
int D, P, S, T, providedNum;
vector<node> record, ill;
unordered_map<string, int> lastGet;
set<string> gotten;
int main()
{
cin >> D >> P;
for (int i = 1; i <= D; i ++ )
{
cin >> T >> S;
record.resize(T);
providedNum = 0;
// 先对每一组的记录进行处理输出, 同时将状态为1的用户添加到ill中, 最后进行输出
for (int j = 0; j < T; j ++ )
{
cin >> record[j].name >> record[j].id >> record[j].isIll >> record[j].time;
record[j].order = j;
if (!isLegal(record[j].id)) record[j].name = "";
else
{
// 添加 ill 信息
if (!lastGet.count(record[j].id)) lastGet[record[j].id] = -100;
if (record[j].isIll == 1 && !gotten.count(record[j].id) )
{
ill.push_back(record[j]);
gotten.insert(record[j].id);
}
}
}
sort(record.begin(), record.end(), cmp);
for (int j = 0; j < T && providedNum < S; j ++ )
{
if (record[j].name != "" && i - lastGet[record[j].id] > P)
{
lastGet[record[j].id] = i;
providedNum ++ ;
cout << record[j].name << " " << record[j].id << endl;
}
}
}
for (auto it : ill) cout << it.name << " " << it.id << endl;
return 0;
}
思路: 在In中保存输入的后序遍历序列,在dfs保存从根节点开始按层序遍历序列的序号。深度优先搜索,index标表示当前节点,大于n则返回。按照完全二叉树的遍历原理进行后序遍历,先进入左右子树(编号为index*2 和 index*2+1,即index右移1位,和index右移1位+1),cnt为后序遍历的位置标记,并将当前所在的后序遍历的元素,填入dfs[index]内
#include
#include
#include
using namespace std;
const int N = 50;
int n, idx, post[N], lev[N];
// 后序 -> 层序 下标表示了层序遍历的顺序
void level(int i)
{
if (i > n) return;
level(i << 1);
level(i << 1 | 1);
lev[i] = post[idx ++ ];
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> post[i];
level(1);
cout << lev[1];
for (int i = 2; i <= n; i ++ ) cout << " " << lev[i];
return 0;
}
思路: 用邻接表来存储图, path表示每一条攻略的路径, 对于每一条攻略, 首先判断其中访问网红点的数量是否等于给定的网红点的数量n, 再判断其中是有网红点被访问过两次, 最后再判断是否有两点之间的路径不可达, 如果上述条件都符合的话, 将累加到的路径与之前保存的最小路径的cost进行比较, 如果更小, 更新mincost和id, 如果相等且 i < id, 更新id
#include
#include
#include
using namespace std;
const int N = 210, INF = 0x3f3f3f3f;
int n, m, k, g[N][N], cnt[N], path[N];
int main()
{
cin >> n >> m;
int a, b, c;
memset(g, INF, sizeof g);
for (int i = 0; i < m; i ++ )
{
cin >> a >> b >> c;
g[a][b] = g[b][a] = c;
}
cin >> k;
int mincost = INF, id = -1, num = 0;
for (int i = 1; i <= k; i ++ )
{
int u, cost = 0;
path[0] = path[n + 1] = 0;
cin >> u;
bool flag = false;
memset(cnt, 0, sizeof cnt);
for (int j = 1; j <= u; j ++ )
{
cin >> path[j];
if (cnt[path[j]]) path[0] ++ ;
cnt[path[j]] ++ ;
}
if (u != n || path[0]) continue;
for (int j = 1; j <= n + 1; j ++ )
{
if (g[path[j]][path[j - 1]] == INF)
{
flag = true;
break;
}
cost += g[path[j]][path[j - 1]];
}
if (flag) continue;
num ++ ;
if (cost < mincost)
{
id = i;
mincost = cost;
} else if (cost == mincost) {
id = min(id, i);
}
}
cout << num << endl << id << " " << mincost << endl;
return 0;
}
/// 另一种做法
#include
#include
#include
using namespace std;
const int maxn = 10010;
vector<int> child[maxn];
//目前序列和结果序列
vector<int> path, ans;
int father[maxn];
int n, k;
int temp;
int maxDepth = 0;
void DFS(int s, int depth)
{
if (child[s].size() == 0)
{
if (depth > maxDepth)
{
maxDepth = depth;
ans = path;
}
return;
}
for (int i = 0; i < child[s].size(); i++)
{
path.push_back(child[s][i]);
DFS(child[s][i], depth + 1);
//记得回退
path.pop_back();
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
father[i] = i;
for (int i = 0; i < n; i++)
{
cin >> k;
for (int j = 0; j < k; j++)
{
cin >> temp;
child[i].push_back(temp);
father[temp] = i;
}
//排序
if (child[i].size() != 0)
sort(child[i].begin(), child[i].end(), less<int>());
}
int root;
for (int i = 0; i < n; i++)
{
if (father[i] == i)
root = father[i];
}
// cout << root << endl;
path.push_back(root);
DFS(root, 1);
cout << maxDepth << endl;
// cout << ans.size() << endl;
for (int i = 0; i < ans.size(); i++)
{
cout << ans[i];
if (i != ans.size() - 1)
cout << " ";
}
return 0;
}
思路: Q[i]内保存每个轨道上的商品,St保存当前筐的状态~接下来按题意进行模拟操作就可以了,注意0操作前判断St是否为空,其余操作前判断相对应的Q[i]是否为空
或者用双栈 – 思路一样
#include
#include
#include
#include
#include
using namespace std;
const int N = 1010;
queue<char> q[N];
stack<char> stk;
int main()
{
int n, m, smax; cin >> n >> m >> smax;
string s;
for (int i = 1; i <= n; i ++ )
{
cin >> s;
for (int j = 0; j < s.size(); j ++ ) q[i].push(s[j]);
}
int op;
while (cin >> op)
{
if (op == -1) break;
if (op == 0)
{
if (stk.empty()) continue;
cout << stk.top();
stk.pop();
} else {
if (q[op].empty()) continue;
if (stk.size() == smax)
{
cout << stk.top();
stk.pop();
}
stk.push(q[op].front());
q[op].pop();
}
}
return 0;
}
思路: dfs搜索, 注意当存入的时候要通过sort排序, p[]数组用来指向各结点的前驱结点, 便于后期查找路径
#include
#include
#include
#include
using namespace std;
const int N = 10010;
bool st[N];
int lev = -1, p[N], id = -1;
void dfs(int u, int v, vector<vector<int> > &vv)
{
if (v > lev)
{
lev = v;
id = u;
}
for (auto t : vv[u])
{
dfs(t, v + 1, vv);
}
}
int main()
{
int n, k, x; cin >> n;
vector<vector<int> > v(n);
memset(p, -1, sizeof p);
for (int i = 0; i < n; i ++ )
{
cin >> k;
for (int j = 0; j < k; j ++ )
{
cin >> x;
p[x] = i;
st[x] = true;
v[i].push_back(x);
}
sort(v[i].begin(), v[i].end());
}
int fi = -1;
for (int i = 0; i < n; i ++ )
if (!st[i]) fi = i;
dfs(fi, 1, v);
cout << lev << endl;
vector<int> ans;
while (id != fi)
{
ans.push_back(id);
id = p[id];
}
ans.push_back(id);
cout << ans[ans.size() - 1];
for (int i = ans.size() - 2; i >= 0; i -- )
{
cout << " " << ans[i];
}
return 0;
}
思路: 将每一个功能模块的输出放在vector中(用temp储存),并将其储存在map容器A内。最后将数量与模块输出储存在可以有多个相同key值的multimap容器B内,因为按照增序数出,所以添加一个容器参数greater。这样一来B内就是排好序的答案啦,然后直接输出答案即可
#include
#include
#include
#include
#include
using namespace std;
const int N = 10010;
int n, m, t;
vector<int> tmp;
map<vector<int>, int> A;
multimap<int, vector<int>, greater<int>> B; // multimap - 允许存在重复键
int main()
{
cin >> n >> m;
tmp.resize(m);
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < m; j ++ ) cin >> tmp[j];
A[tmp] ++ ;
}
for (auto it : A) B.insert({it.second, it.first});
cout << A.size() << endl;
for (auto it : B)
{
cout << it.first;
for (auto it2 : it.second) cout << " " << it2;
cout << endl;
}
return 0;
}
思路: 直接模拟
#include
#include
#include
#include
using namespace std;
const int N = 100010;
vector<int> v[N];
int archive[110];
int main()
{
int n, m, k; cin >> n >> m;
int x;
for (int i = 1; i <= n; i ++ )
{
cin >> k;
for (int j = 1; j <= k; j ++ )
{
cin >> x;
v[i].push_back(x);
}
}
int op, idx = 1;
for (int i = 1; i <= m; i ++ )
{
cin >> op >> x;
if (op == 0)
{
idx = v[idx][x - 1];
} else if (op == 1) {
archive[x] = idx;
cout << idx << endl;
} else {
idx = archive[x];
}
}
cout << idx << endl;
return 0;
}
思路: 01背包问题,因为要输出从小到大的排列,可以先把硬币面额从大到小排列,然后用bool类型的choice[i][j]数组dp[i][j]是否选取,如果选取了就令choice为true;然后进行01背包问题求解,如果最后求解的结果不是恰好等于所需要的价值的,就输出No Soultion,否则从choice[i][j]判断选取的情况,i从n到1表示从后往前看第i个物品的选取情况,j从m到0表示从容量m到0是否选取(j = j – w[i]),把选取情况压入res数组中,最后输出res数组
#include
#include
#include
#include
using namespace std;
const int N = 10010;
int f[N], w[N]; // 一维滚动数组
bool st[N][N];
int cmp(int a, int b) {return a > b; }
int main()
{
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
sort(w + 1, w + n + 1, cmp);
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= w[i]; j -- )
if (f[j] <= f[j - w[i]] + w[i]) {
st[i][j] = true; // 选取
f[j] = f[j - w[i]] + w[i];
}
if (f[m] != m) puts("No Solution");
else {
vector<int> res;
int v = m, idx = n;
while (v > 0) {
if (st[idx][v]) {
res.push_back(w[idx]);
v -= w[idx];
}
idx -- ;
}
for (int i = 0; i < res.size(); i ++ ) {
cout << res[i];
if (i == res.size() - 1) cout << endl;
else cout << " ";
}
}
return 0;
}
思路: 分析:并查集。先写好init、findFather、Union。
0. 每个社交圈的结点号是人的编号,而不是课程。课程是用来判断是否处在一个社交圈的。
1. course[t]表示任意一个喜欢t活动的人的编号。如果当前的课程t,之前并没有人喜欢过,那么就course[t] = i,i为它自己的编号,表示i为喜欢course[t]的一个人的编号
2. course[t]是喜欢t活动的人的编号,那么findFather(course[t])就是喜欢这个活动的人所处的社交圈子的根结点,合并根结点和当前人的编号的结点i。即Union(i, findFather(course[t])),把它们处在同一个社交圈子里面
3. isRoot[i]表示编号i的人是不是它自己社交圈子的根结点,如果等于0表示不是根结点,如果不等于0,每次标记isRoot[findFather(i)]++,那么isRoot保存的就是如果当前是根结点,那么这个社交圈里面的总人数
4. isRoot中不为0的编号的个数cnt就是社交圈圈子的个数
5. 把isRoot从大到小排列,输出前cnt个,就是社交圈人数的从大到小的输出顺序
#include
#include
#include
using namespace std;
const int N = 1010;
int p[N], f[N], root[N]; // f 为喜欢第i个编号的人
int cmp(int a, int b) {return a > b; }
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void Union(int a, int b)
{
p[find(a)] = find(b);
}
int main()
{
int n, k, t, cnt = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 1; i <= n; i ++ ) {
scanf("%d:", &k);
for (int j = 1; j <= k; j ++ )
{
scanf("%d", &t);
if (f[t] == 0) f[t] = i;
Union(i, find(f[t]));
}
}
for (int i = 1; i <= n; i ++ ) root[find(i)] ++ ;
for (int i = 1; i <= n; i ++ ) {
if (root[i]) cnt ++ ;
}
printf("%d\n", cnt);
sort(root, root + N, cmp);
for (int i = 0; i < cnt; i ++ ) {
printf("%d", root[i]);
if (i == cnt - 1) printf("\n");
else printf(" ");
}
return 0;
}
思路: 三维的广度优先搜索~XYZ三个数组判断方向,对每一个点广度优先累计肿瘤块的大小,如果大于等于t就把结果累加。用visit数组标记当前的点有没有被访问过,被访问过的结点是不会再访问的。。judge判断是否超过了边界,或者是否当前结点为0不是肿瘤
#include
#include
#include
#include
using namespace std;
const int N = 1010;
struct node {
int x, y, z;
};
int m, n, l, t;
int dx[6] = {1, 0, 0, -1, 0, 0};
int dy[6] = {0, 1, 0, 0, -1, 0};
int dz[6] = {0, 0, 1, 0, 0, -1};
int g[1300][130][80];
bool st[1300][130][80];
bool judge(int x, int y, int z) {
if (x < 0 || x >= m || y < 0 || y >= n || z < 0 || z >= l) return false;
if (g[x][y][z] == 0 || st[x][y][z]) return false;
return true;
}
int bfs(int x, int y, int z) {
int cnt = 0;
node tmp;
tmp.x = x, tmp.y = y, tmp.z = z;
queue<node > q;
q.push(tmp);
st[x][y][z] = true;
while (!q.empty()) {
node top = q.front(); q.pop();
cnt ++ ;
for (int i = 0; i < 6; i ++ ) {
int tx = top.x + dx[i];
int ty = top.y + dy[i];
int tz = top.z + dz[i];
if (judge(tx, ty, tz)) {
st[tx][ty][tz] = true;
tmp.x = tx, tmp.y = ty, tmp.z = tz;
q.push(tmp);
}
}
}
if (cnt >= t) return cnt;
else return 0;
}
int main()
{
cin >> m >> n >> l >> t;
// 注意输入的顺序
for (int i = 0; i < l; i ++ )
for (int j = 0; j < m; j ++ )
for (int k = 0; k < n; k ++ )
cin >> g[j][k][i];
int res = 0;
for (int i = 0; i < l; i ++ )
for (int j = 0; j < m; j ++ )
for (int k = 0; k < n; k ++ )
if (g[j][k][i] && !st[j][k][i]) {
res += bfs(j, k, i);
}
cout << res << endl;
return 0;
}
思路: 因为垃圾站之间也是彼此有路连接的,所以最短路径计算的时候也要把垃圾站算上。所以我们就是堆n+m个点进行Dijkstra计算最短路径。要求计算出1~m号垃圾站距离其他站点的最短路径。这时候可以遍历dis数组,如果dis存在一个距离大于服务范围ds的距离,那么我们就舍弃这个垃圾站。取最最短的路径,这就是距离它最近的垃圾站mindis。如果mindis > ansdis,就是说找到了一个距离居民最小距离的垃圾站是更远的,那就选这个垃圾站,更新ansid为它的id。最后输出
对于垃圾站的字符串编号的处理:如果最近居民区最大的值没有变化但是找到了一个更小的平均距离,那就选这个。我们可以根据输入的是G还是数字,如果是数字就令编号为他自己,如果是G开头的,编号设为n+G后面的数字
#include
#include
#include
#include
using namespace std;
const int inf = 999999999;
int n, m, k, ds, station;
int e[1020][1020], dis[1020];
bool visit[1020];
int main() {
fill(e[0], e[0] + 1020 * 1020, inf);
fill(dis, dis + 1020, inf);
scanf("%d%d%d%d", &n, &m, &k, &ds);
for(int i = 0; i < k; i++) {
int tempdis;
string s, t;
cin >> s >> t >> tempdis;
int a, b;
if(s[0] == 'G') {
s = s.substr(1);
a = n + stoi(s);
} else {
a = stoi(s);
}
if(t[0] == 'G') {
t = t.substr(1);
b = n + stoi(t);
} else {
b = stoi(t);
}
e[a][b] = tempdis;
e[b][a] = tempdis;
}
int ansid = -1;
double ansdis = -1, ansaver = inf;
for(int index = n + 1; index <= n + m; index++) {
double mindis = inf, aver = 0;
fill(dis, dis + 1020, inf);
fill(visit, visit + 1020, false);
dis[index] = 0;
for(int i = 0; i < n + m; i++) {
int u = -1, minn = inf;
for(int j = 1; j <= n + m; j++) {
if(visit[j] == false && dis[j] < minn) {
u = j;
minn = dis[j];
}
}
if(u == -1) break;
visit[u] = true;
for(int v = 1; v <= n + m; v++) {
if(visit[v] == false && dis[v] > dis[u] + e[u][v])
dis[v] = dis[u] + e[u][v];
}
}
for(int i = 1; i <= n; i++) {
if(dis[i] > ds) {
mindis = -1;
break;
}
if(dis[i] < mindis) mindis = dis[i];
aver += 1.0 * dis[i];
}
if(mindis == -1) continue;
aver = aver / n;
if(mindis > ansdis) {
ansid = index;
ansdis = mindis;
ansaver = aver;
} else if(mindis == ansdis && aver < ansaver) {
ansid = index;
ansaver = aver;
}
}
if(ansid == -1)
printf("No Solution");
else {
printf("G%d\n", ansid - n);
printf("%.1f %.1f", ansdis, ansaver);
}
return 0;
}
思路:
思路: 用二维数组,结点push_back的方式存储,因为每次都从1遍历到n会超时,而只要遍历到v[top].size()就不会超时。
用广度优先搜索,level记录当前结点的层数,level为自己的上一层结点的level+1,遇到更大层数的时候就令ans又为最大值,让ans取当层的最小值(在不等于被搜索结点本身的时候)。book记录当前结点有没有被访问过,如果没有被访问过就压入队列中,因为可能重复然后导致无限循环,无法输出
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 10010;
vector<vector<int> > g(N);
bool st[N];
int lev[N];
int bfs(int t)
{
memset(st, false, sizeof st);
memset(lev, 0, sizeof lev);
int maxlev = -1, id = N;
queue<int> q;
q.push(t);
st[t] = true;
while (!q.empty()) {
int h = q.front(); q.pop();
if (lev[h] > maxlev) {
maxlev = lev[h];
id = N;
}
if (h != t)
id = min(id, h);
for (int i = 0; i < g[h].size(); i ++ ) {
if (!st[g[h][i]]) {
st[g[h][i]] = true;
q.push(g[h][i]);
lev[g[h][i]] = lev[h] + 1;
}
}
}
if (id != N) return id;
else return 0;
}
int main()
{
int n, m, k; cin >> n >> m >> k;
int a, b;
for (int i = 0; i < m; i ++ ) {
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
int t;
for (int i = 0; i < k; i ++ ) {
cin >> t;
cout << bfs(t) << endl;
}
return 0;
}
思路: 根据二叉树的性质,i结点的左孩子为root<<1,右孩子为root<<1|1.建造并用数组存储这个二叉搜索树,然后判断是否是完全二叉树
#include
using namespace std;
int tree[1<<20];
int num;
void BST(int a) {
if(tree[a] == 0)
tree[a] = num;
else if(tree[a] < num)
BST(a<<1);
else
BST(a<<1|1);
}
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &num);
BST(1);
}
bool flag = true;
for(int cnt = 1, k = 1; cnt <= n; k++) {
if(tree[k] == 0)
flag = false;
else {
printf("%d", tree[k]);
if(cnt++ != n) printf(" ");
}
}
if(flag) printf("\nYES");
else printf("\nNO");
return 0;
}
思路: 多条件Dijkstra最短路
#include
using namespace std;
const int N = 210, inf = 0x3f3f3f3f;
int n, m;
int g[N][N], dist[N];
int num[N], pre[N], sum[N], cnt[N], city[N];
bool st[N];
void dijkstra(int u)
{
memset(dist, 0x3f, sizeof dist);
dist[u] = 0;
cnt[u] = city[u] = 1;
for (int i = 0; i < n; i ++ ) {
int t = -1;
for (int j = 0; j < n; j ++ ) {
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
}
st[t] = true;
for (int j = 0; j < n; j ++ ) {
if (dist[j] > dist[t] + g[t][j]) {
dist[j] = dist[t] + g[t][j];
pre[j] = t;
cnt[j] = cnt[t];
sum[j] = sum[t] + num[j];
city[j] = city[t] + 1;
} else if (dist[j] == dist[t] + g[t][j]) {
cnt[j] += cnt[t];
if (city[j] < city[t] + 1) {
pre[j] = t;
sum[j] = sum[t] + num[j];
city[j] = city[t] + 1;
} else if (city[j] == city[t] + 1) {
if (sum[j] < sum[t] + num[j]) {
pre[j] = t;
sum[j] = sum[t] + num[j];
}
}
}
}
}
}
int main()
{
string s, t;
cin >> n >> m >> s >> t;
memset(g, 0x3f, sizeof g);
unordered_map<string, int> stoi;
unordered_map<int, string> itos;
stoi[s] = 0, itos[0] = s;
for (int i = 1; i < n; i ++ ) {
string s1; int x;
cin >> s1 >> x;
stoi[s1] = i;
itos[i] = s1;
num[i] = sum[i] = x;
}
while (m -- ) {
string s1, s2; int a, b, c;
cin >> s1 >> s2 >> c;
a = stoi[s1], b = stoi[s2];
g[a][b] = g[b][a] = c;
}
int sta = stoi[s], ed = stoi[t];
dijkstra(sta);
vector<int> ans;
int b = ed;
while (sta != ed) {
ans.push_back(ed);
ed = pre[ed];
}
cout << itos[sta];
for (int i = ans.size()-1; i >= 0; i -- )
cout << "->" << itos[ans[i]];
cout << endl;
cout << cnt[b] << ' ' << dist[b] << ' ' << sum[b];
return 0;
}
思路:
思路:
思路:
思路:
思路:
思路:
思路:dfs暴搜
#include
using namespace std;
const int N = 100010;
int n, m, k, t; bool flag1, flag2;
int h[N]; bool st[N];
vector<int> ans, frag[110];
void dfs(int p)
{
if (ans.size() == m) {
flag1 = true;
for (int i = 0; i < m; i ++ ) {
if (i) cout << ' ';
cout << ans[i];
}
}
if (flag1) return;
for (int i = 1; i <= m; i ++ ) {
if (st[i]) continue;
flag2 = 0;
for (int j = 0; j < frag[i].size(); j ++ ) {
if (frag[i][j] != h[p+j]) {
flag2 = true;
break;
}
}
if (flag2) continue;
ans.push_back(i);
st[i] = 1;
dfs(p + frag[i].size()-1);
st[i] = 0;
ans.pop_back();
}
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> h[i];
cin >> m;
for (int i = 1; i <= m; i ++ ) {
cin >> k;
for (int j = 0; j < k; j ++ ) {
cin >> t;
frag[i].push_back(t);
}
}
dfs(0);
return 0;
}