A - 掌握魔法の东东 II
从瑞神家打牌回来后,东东痛定思痛,决定苦练牌技,终成赌神!
东东有 A × B 张扑克牌。每张扑克牌有一个大小(整数,记为a,范围区间是 0 到 A - 1)和一个花色(整数,记为b,范围区间是 0 到 B - 1。
扑克牌是互异的,也就是独一无二的,也就是说没有两张牌大小和花色都相同。
“一手牌”的意思是你手里有5张不同的牌,这 5 张牌没有谁在前谁在后的顺序之分,它们可以形成一个牌型。 我们定义了 9 种牌型,如下是 9 种牌型的规则,我们用“低序号优先”来匹配牌型,即这“一手牌”从上到下满足的第一个牌型规则就是它的“牌型编号”(一个整数,属于1到9):
同花顺: 同时满足规则 2 和规则 3.
顺子 : 5张牌的大小形如 x, x + 1, x + 2, x + 3, x + 4
同花 : 5张牌都是相同花色的.
炸弹 : 5张牌其中有4张牌的大小相等.
三带二 : 5张牌其中有3张牌的大小相等,且另外2张牌的大小也相等.
两对: 5张牌其中有2张牌的大小相等,且另外3张牌中2张牌的大小相等.
三条: 5张牌其中有3张牌的大小相等.
一对: 5张牌其中有2张牌的大小相等.
要不起: 这手牌不满足上述的牌型中任意一个.
现在, 东东从A × B 张扑克牌中拿走了 2 张牌!分别是 (a1, b1) 和 (a2, b2). (其中a表示大小,b表示花色)
现在要从剩下的扑克牌中再随机拿出 3 张!组成一手牌!!
其实东东除了会打代码,他业余还是一个魔法师,现在他要预言他的未来的可能性,即他将拿到的“一手牌”的可能性,我们用一个“牌型编号(一个整数,属于1到9)”来表示这手牌的牌型,那么他的未来有 9 种可能,但每种可能的方案数不一样。
现在,东东的阿戈摩托之眼没了,你需要帮他算一算 9 种牌型中,每种牌型的方案数。
Input
第 1 行包含了整数 A 和 B (5 ≤ A ≤ 25, 1 ≤ B ≤ 4).
第 2 行包含了整数 a1, b1, a2, b2 (0 ≤ a1, a2 ≤ A - 1, 0 ≤ b1, b2 ≤ B - 1, (a1, b1) ≠ (a2, b2)).
Output
输出一行,这行有 9 个整数,每个整数代表了 9 种牌型的方案数(按牌型编号从小到大的顺序)
Examples
Input
5 2
1 0 3 1
Output
0 8 0 0 0 12 0 36 0
Input
25 4
0 0 24 3
Output
0 0 0 2 18 1656 644 36432 113344
这道题我只想到了暴力搜索的解法,题中给出了两个卡片,还剩3张卡片需要我们从剩余的组合中挑选,用3层for循环来挑选,但注意要舍弃挑选到的相同的卡片。将生成的卡片种类按照从1到9的规则投入相应的方案中即可。
#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
int a1, b1, a2, b2;
int A, B;
long long out1 = 0;
long long out2 = 0;
long long out3 = 0;
long long out4 = 0;
long long out5 = 0;
long long out6 = 0;
long long out7 = 0;
long long out8 = 0;
long long out9 = 0;
bool legal(int i1, int j1, int i2, int j2)
{
//两个坐标点不重合
if (i1 != i2 || j1 != j2)
{
return true;
}
return false;
}
bool rule2(int i1, int j1, int i2, int j2, int i3, int j3)
{
//顺子 : 5张牌的大小形如 x, x + 1, x + 2, x + 3, x + 4
int a[5];
a[0] = i1;
a[1] = i2;
a[2] = i3;
a[3] = a1;
a[4] = a2;
sort(a, a + 5);
if ((a[1] - a[0] == 1) && (a[2] - a[1] == 1) && (a[3] - a[2] == 1) && (a[4] - a[3] == 1))
{
return true;
}
return false;
}
bool rule3(int i1, int j1, int i2, int j2, int i3, int j3)
{
//同花 : 5张牌都是相同花色的.
if ((b1 == b2) && (b2 == j1) && (j1 == j2) && (j2 == j3))
{
return true;
}
return false;
}
void my_count(int i1, int i2, int i3,int* s)
{
int* a = new int[A];//创建大小为A的整形数组
for (int i = 0;i < A;i++)
{
a[i] = 0;
}
a[a1]++;
a[a2]++;
a[i1]++;
a[i2]++;
a[i3]++;
int num = 0;
for (int i = 0;i < A;i++)
{
if (a[i] != 0)
{
s[num] = a[i];//num不会超过4
num++;
}
}
delete[]a;
}
void rule(int i1, int j1, int i2, int j2, int i3, int j3)
{
if (rule2(i1, j1, i2, j2, i3, j3) && rule3(i1, j1, i2, j2, i3, j3))
{
out1++;
}
else if (rule2(i1, j1, i2, j2, i3, j3))
{
out2++;
}
else if (rule3(i1, j1, i2, j2, i3, j3))
{
out3++;
}
else
{
int s[5];
for (int i = 0;i < 5;i++)
{
s[i] = 0;
}
my_count(i1, i2, i3, s);//s1 s2 s3 s4 s5统计五张牌中大小相同的牌的个数
sort(s, s + 5);
if (s[4] == 4)//最大的频率一定储存在s数组中的第四个
{
out4++;
}
//s数组储存5个数字出现的频率,按由小到大的顺序存储,最大不超过4
else if (s[3] == 2 && s[4] == 3)
{
out5++;
}
else if (s[3] == 2 && s[4] >= 2)
{
out6++;
}
else if (s[4] >= 3)
{
out7++;
}
else if (s[4] >= 2)
{
out8++;
}
else
{
out9++;
}
}
}
int main()
{
cin >> A >> B;
cin >> a1 >> b1 >> a2 >> b2;
for (int i1 = 0;i1 < A;i1++)
{
for (int j1 = 0;j1 < B;j1++)
{
if (legal(i1, j1, a1, b1) && legal(i1, j1, a2, b2))
{
for (int i2 = 0;i2 < A;i2++)
{
for (int j2 = 0;j2 < B;j2++)
{
if (legal(i2, j2, a1, b1) && legal(i2, j2, a2, b2) && legal(i1, j1, i2, j2))
{
for (int i3 = 0;i3 < A;i3++)
{
for (int j3 = 0;j3 < B;j3++)
{
if (legal(i3, j3, a1, b1) && legal(i3, j3, a2, b2) && legal(i1, j1, i3, j3) && legal(i2, j2, i3, j3))
{
rule(i1, j1, i2, j2, i3, j3);
}
}
}
}
}
}
}
}
}
cout << out1 / 6 << " " << out2 / 6 << " " << out3 / 6 << " " << out4 / 6 << " " << out5 / 6 << " " << out6 / 6 << " " << out7 / 6 << " " << out8 / 6 << " " << out9 / 6 << " " << endl;
return 0;
}
A - 氪金带东
实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.
Input
输入文件包含多组测试数据。对于每组测试数据,第一行一个整数N (N<=10000),接下来有N-1行,每一行两个数,对于第i行的两个数,它们表示与i号电脑连接的电脑编号以及它们之间网线的长度。网线的总长度不会超过10^9,每个数之间用一个空格隔开。
Output
对于每组测试数据输出N行,第i行表示i号电脑的答案 (1<=i<=N).
Sample Input
5
1 1
2 1
3 1
1 1
Sample Output
3
2
3
4
4
此题为求图中离每一点最远点的距离,首先要找出图的直径,即随意从某点开始找出距离此点最远的端点,再从这个端点出发找到距离此端点最远的另一个端点即为直径的另一端。从这两个端点出发记录各点到这两个端点的距离,选最大的距离作为该点到图中最远点的距离。即经过3次bfs或dfs即可得出结果。
#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<queue>
#define MAXM 10000
using namespace std;
struct Edge {
int u;
int v;
int w;
int next;
};
int vis[MAXM + 1];//记录顶点是否已被访问
int N;
int head[MAXM + 1];//
int tot = 0;
int dis[MAXM + 1], dis1[MAXM + 1], dis2[MAXM + 1];
Edge edge[MAXM * 2 + 2];//记录边与边之间的权重关系
int sum = 0;
int vertex1, vertex2;//分别记录直径的两个顶点
int vertex;
void init(int n)
{
tot = 0;
for (int i = 1;i <= n;i++)
{
head[i] = -1;
}
}
void addedge(int u, int v, int w)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot;
tot++;
}
//定点的范围为1到N
void bfs(int begin)
{
for (int i = 1;i <= MAXM;i++)
{
dis[i] = 0;
vis[i] = 0;
}
queue<int> q;
dis[begin] = 0;
vis[begin] = 1;
q.push(begin);
//cout << "begin=" << begin << endl;
int distance = 0;
vertex=begin;
while (!q.empty())
{
int tmp = q.front();
q.pop();
for (int t = head[tmp];t != -1;t = edge[t].next)
{
// cout << "v=" << v << endl;
if (!vis[edge[t].v])
{
vis[edge[t].v] = 1;
dis[edge[t].v] = dis[tmp] + edge[t].w;
//进入队列的一定是以前没有被访问的且距离初始点的距离已知
if (distance < dis[edge[t].v])
{
distance = dis[edge[t].v];
vertex = edge[t].v;
}
q.push(edge[t].v);
}
}
}
}
int main()
{
//cin >> N;
while(scanf("%d", &N)!=EOF)
{
init(N);
int v, w;
for (int i = 2;i <= N;i++)
{
cin >> v >> w;
addedge(i, v, w);
addedge(v, i, w);
}
bfs(1);
bfs(vertex);
for (int i = 1;i <= N;i++)
{
dis1[i] = dis[i];
}
bfs(vertex);
for (int i = 1;i <= N;i++)
{
dis2[i] = dis[i];
}
for (int i = 1;i <= N;i++)
{
printf("%d\n", max(dis1[i], dis2[i]));
}
}
return 0;
}
B - 戴好口罩!
新型冠状病毒肺炎(Corona Virus Disease 2019,COVID-19),简称“新冠肺炎”,是指2019新型冠状病毒感染导致的肺炎。
如果一个感染者走入一个群体,那么这个群体需要被隔离!
小A同学被确诊为新冠感染,并且没有戴口罩!!!!!!
危!!!
时间紧迫!!!!
需要尽快找到所有和小A同学直接或者间接接触过的同学,将他们隔离,防止更大范围的扩散。
众所周知,学生的交际可能是分小团体的,一位学生可能同时参与多个小团体内。
请你编写程序解决!戴口罩!!
Input
多组数据,对于每组测试数据:
第一行为两个整数n和m(n = m = 0表示输入结束,不需要处理),n是学生的数量,m是学生群体的数量。0 < n <= 3e4 , 0 <= m <= 5e2
学生编号为0~n-1
小A编号为0
随后,m行,每行有一个整数num即小团体人员数量。随后有num个整数代表这个小团体的学生。
Output
输出要隔离的人数,每组数据的答案输出占一行
Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
本题可用并查集的思想,找出每个同学所属的集合并计算该集合的大小,第0号同学参加的团体中的同学也可能参加另外的团体,如果用其他的方法可能复杂度太高。采用并查集可极大地缩短时间复杂度。本题采用的并查集中还包含了路径优化的思想,极大地缩短了查找时间。
#include<iostream>
using namespace std;
const int maxn = 300000 + 1;
const int maxm = 5000 + 1;
int par[maxn];
int sum[maxn];//记录相应集合的大小
int n, m;//n是学生的数量,m是学生群体的数量
int num;//需要隔离的总人数
void init(int n)
{
for (int i = 0;i < n;i++)
{
par[i] = i;
sum[i] = 1;
}
}
//一边找x的祖先,一边缩短路径
int find(int x)
{
if (par[x] == x)
{
return x;
}
return par[x] = find(par[x]);
}
void unit(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
{
return;
}
if (sum[x] > sum[y])
{
int t = y;
y = x;
x = t;
}
par[x] = y;
sum[y] = sum[x] + sum[y];
// cout << "x=" << x << " y=" << y << endl;
// cout << "sum[x]=" << sum[x] << " sum[y]=" << sum[y] << endl;
// cout<<
}
int main()
{
while (true)
{
cin >> n >> m;
int x, y;
if (n == 0 && m == 0)
{
break;
}
init(n);
int numm;
for (int i = 0;i < m;i++)
{
cin >> numm;
int x,y;
cin >> x;
for (int j = 1;j <= numm - 1;j++)
{
cin >> y;
unit(x, y);
x = y;
}
}
num = sum[find(0)];
cout << num << endl;
}
return 0;
}
C - 掌握魔法の东东 I
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌氵
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
东东为所有的田灌氵的最小消耗
Input
第1行:一个数n
第2行到第n+1行:数wi
第n+2行到第2n+1行:矩阵即pij矩阵
Output
东东最小消耗的MP值
Example
Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Output
9
本题可将题中所给的农田和“黄河之水天上来”看做n+1个顶点,按照所给定点和所给边找出一种方案使总耗费最小。本题承袭上题,同样采用并查集的思想,采用最小生成树算法,首先找到图中的最短边,如果该边的两个顶点已经在最小生成树中了,则放弃,否则将该边加入最小生成树中,将两个顶点加入并查集中。直到加入n条边为止。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 3000 + 1;//农田编号
int w[maxn];//黄河之水天上来的消耗
const int maxm = 100000 + 1;
int par[maxn];
int sum[maxn];//记录相应集合的大小
int n;//n是农田的数量
int num;//需要的最小消耗
struct Array0 {
int i;
int j;
int w;
};
Array0 a[maxn * maxn];
void init(int n)
{
for (int i = 0;i < n + 1;i++)
{
par[i] = i;
sum[i] = 1;
}
}
//一边找x的祖先,一边缩短路径
int find(int x)
{
if (par[x] == x)
{
return x;
}
return par[x] = find(par[x]);
}
bool unit(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
{
return false;
}
if (sum[x] > sum[y])
{
int t = y;
y = x;
x = t;
}
par[x] = y;
sum[y] = sum[x] + sum[y];
return true;
}
bool cmp(Array0 a, Array0 b)
{
if (a.w < b.w)
{
return true;
}
return false;
}
/*bool Con()
{
for (int i = 1;i < n + 1;i++)
{
if (find(0) != find(i))
{
return false;
}
}
return true;
}*/
int main()
{
cin >> n;
init(n + 1);
int size = 0;
int tmp;
for (int i = 0;i < n;i++)
{
cin >> tmp;
if (tmp != 0)
{
a[size].i = i;
a[size].j = n;
a[size].w = tmp;
size++;
}
}
for (int i = 0;i < n;i++)
{
for (int j = 0;j < n;j++)
{
cin >> tmp;
if (tmp != 0)
{
a[size].i = i;
a[size].j = j;
a[size].w = tmp;
size++;
}
}
}
sort(a, a + size, cmp);
// for (int i = 0;i < size;i++)
// {
// cout << a[i].i << " " << a[i].j << " " << a[i].w << endl;
// }
int key = 0;
int i, j;
num = 0;
int k = 0;
while (true)
{
i = a[key].i;
j = a[key].j;
if (unit(i, j) == false)
{
key++;
continue;
}
k++;
// cout << "取出第" << i << "号和第" << j << "号农田" << endl;
num += a[key].w;
key++;
// cout << "key=" << key << endl;
if (k == n)
{
cout << num << endl;
break;
}
}
return 0;
}
D - 数据中心
Example Input 4 5 1 1 2 3 1 3 4 1 4 5 2 3 8 3 4 2 Output 4
本题还是一个最小生成树的问题,这里的最小是指树的最大边权重最小,由n个点所连成的最小连通图必然是一棵树,必然有n-1条边入选,但要注意入选的每一条边的两个顶点不能已经在原先的最小连通图,这里就可以用并查集的思想。如果两个顶点的祖先顶点相同,则这两个顶点已在最小连通图中,反之,则可以将这条边加入到最小连通图中。
#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<queue>
#define MAXN 50000
# define MAXM 100000
using namespace std;
struct Edge {
int u;
int v;
int w;
bool operator<(const Edge& t)
{
return w < t.w;
}
}edge[MAXM + 10];
int n, m, root;
int par[MAXN];
void init(int n)
{
for (int i = 1;i <= n;i++)
{
par[i] = i;
}
}
int find(int x)
{
if (x == par[x])
{
return x;
}
return par[x] = find(par[x]);
}
bool unit(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
{
return false;
}
par[x] = y;//x的祖先是y
return true;
}
int kruskal_max()
{
sort(edge + 1, edge + m + 1);
int key = 0;
int ans = 0;
for (int i = 1;i < m;i++)
{
if (unit(edge[i].u, edge[i].v))
{
key++;
ans = max(ans, edge[i].w);
if (key == n - 1)
{
return ans;
break;
}
}
}
}
int main()
{
cin >> n >> m >> root;//输入顶点,边以及根
init(n);
for (int i = 1;i <= m;i++)
{
cin >> edge[i].u >> edge[i].v >> edge[i].w;
}
int max_ = kruskal_max();
cout << max_;
return 0;
}