C - Bob’s Problem
题目大意就是有黑边和白边,白边有数量限制,但是黑边没有,问连通图的最大权值和(注意已经联通之后也可以加边)。就是一个最大生成树的问题,但是需要贪心求解。
需要注意的是题目说了在一些顶点之间允许有多条边,并且允许自环,首先自环不影响联通性,而且因为题目保证边权值是非负数整数,所以所有的自环我们都需要加上。
首先黑边只要有我们都可以连上,因为黑边只要少连了一条我们的权值和都会减少(前提是权值非负)。然后将所有的白边排序,贪心的从权值大的边开始判断,如果这条边可以使当前图的联通块减少的话,就加入这条边,将题目给的白边数量减一,并标记这条边。
这里为什么要优先将可以使图联通的白边加入,而不是将权值最大的白边加入呢?因为白边的可用数量有限,如果优先将权值最大的白边加入的话,有可能导致本来可以联通的图变得不能联通了,如下样例:
5
4 4 1
1 2 10 0
2 3 6 0
1 3 8 1
3 4 3 1
实际上答案应该是 10 + 6 + 3
如果for一遍白边进行上述操作之后题目给的白边的数量还没有用完,就再贪心的遍历一遍所有白边,如果这条边没有被标记过,就将这条边加入,且将题目给白边数量减一,直到题目给的白边数量用完或者遍历完所有白边。
易错点:
当时做这道题的时候很容易就看出来是一道最大生成树问题,但是细节地方一直没该明白,比如下面的样例中,一条边既是白边,又是黑边,那么实际上最后的答案是 29,也就是说两个点之间不止可以加入一条边!
还有就是判断是否联通不能够按加入的边的数量是否等于n - 1来判断,因为这道题里即使联通了,依然可以加边,也就是说有n个点,但是联通时不一定有多少条边,所以最后需要判断一下是否只有一个联通块。
如下样例:
5
4 4 2
1 2 10 1
1 2 10 0
2 3 6 1
3 4 3 0
AC代码:
#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define fi first
#define se second
#define endl '\n'
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
int read()
{
int x=0;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x;
}
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
return;
}
const int M = 5e5 + 10;
const int N = 5e4 + 100;
int n, m, k;
int p[N], rk[N], bj[M];
struct node
{
int a, b;
int w;
int color;
}edge[M];
void init()
{
for(int i = 1; i <= n; i ++)
p[i] = i, rk[i] = 0;
memset(bj, 0, sizeof bj);
}
bool cmp(node a, node b)
{
return a.w > b.w;
}
int find(int x)
{
int son = x, tem;
while(p[x] != x) x = p[x];
while(son != x)
{
tem = p[son];
p[son] = x;
son = tem;
}
return x;
}
void unit(int r1, int r2)
{
if(rk[r1] < rk[r2])
p[r1] = r2;
else if(rk[r2] > rk[r1])
p[r2] = r1;
else
p[r2] = r1, rk[r1] ++;
}
int main()
{
int t;
t = read();
while(t --)
{
vector<node> P;
ll ans = 0, flag = 0;
n = read(), m = read(), k = read();
init();
for(int i = 1; i <= m; i ++)
{
edge[i].a = read(), edge[i].b = read(), edge[i].w = read(), edge[i].color = read();
int r1 = find(edge[i].a), r2 = find(edge[i].b);
if(edge[i].color) P.push_back(edge[i]);
else ans += edge[i].w, unit(r1, r2);
}
sort(P.begin(), P.end(), cmp);
for(int i = 0; i < P.size() && k; i ++)
{
node now = P[i];
int r1 = find(now.a), r2 = find(now.b);
if(r1 != r2)
{
k --;
ans += now.w;
unit(r1, r2);
bj[i] = 1;
}
}
for(int i = 0; i < P.size() && k; i ++)
if(bj[i] == 0) k --, ans += P[i].w;
for(int i = 1; i <= n; i ++) if(p[i] == i) flag ++; //判断联通块的个数
if(flag == 1) printf("%lld", ans);
else write(-1);
puts("");
}
return 0;
}
B-Who is the Champion
题意难读,结构体排序即可。
#include<bits/stdc++.h>
#define endl '\n'
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N = 5e5 + 10;
int a[150][150];
struct node
{
int sco;
int win;
int bh;
}p[150];
bool cmp(node a, node b)
{
if(a.sco == b.sco) return a.win > b.win;
else return a.sco > b.sco;
}
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++)
{
p[i].bh = i, p[i].sco = 0, p[i].win = 0;
for(int j = 1; j <= n; j ++)
cin >> a[i][j];
}
if(n == 1)
{
cout << 1 << endl;
return 0;
}
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
if(i == j) continue;
if(a[i][j] > a[j][i])
{
p[i].sco += 3;
p[i].win += (a[i][j] - a[j][i]);
p[j].win += (a[j][i] - a[i][j]);
}
else if(a[i][j] == a[j][i])
{
p[i].sco += 1;
p[j].sco += 1;
}
else
{
p[j].sco += 3;
p[i].win += (a[i][j] - a[j][i]);
p[j].win += (a[j][i] - a[i][j]);
}
}
sort(p + 1, p + n + 1, cmp);
if(p[1].sco == p[2].sco && p[1].win == p[2].win) cout << "play-offs" << endl;
else cout << p[1].bh << endl;
return 0;
}