第十周的题

1、

题目:P1525 [NOIP2010 提高组] 关押罪犯

S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为1∼N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

思路:

使用 Kruskal 算法来求解最小生成树问题。基于贪心,通过不断选择权值最小且不会产生环路的边来构建最小生成树。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,f[40001],x,y;
struct data
{
    int a,b,c;
} e[100001];
int gz(const data &a,const data &b)
{
    if(a.c>b.c)return 1;
    else return 0;
}
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1; i<=m; i++)
        scanf("%d %d %d",&e[i].a,&e[i].b,&e[i].c);
    for(int i=1; i<=n*2; i++)
        f[i]=i;
    sort(e+1,e+m+1,gz);
    for(int i=1; i<=m; i++)
    {
        x=find(e[i].a);
        y=find(e[i].b);
        if(x==y)
        {
            printf("%d\n",e[i].c);
            return 0;
        }
        f[y] = find(e[i].a + n);
        f[x] = find(e[i].b + n);
    }
    puts("0");
    return 0;
}

2、

题目:P3386 【模板】二分图最大匹配

给定一个二分图,其左部点的个数为 n,右部点的个数为 m,边数为 e,求其最大匹配的边数。

左部点从 1 至 n 编号,右部点从 1 至 m 编号。

思路: graph 数组用于存储二分图的邻接表。使用 bfs 函数进行广度优先搜索,计算每个未匹配点到左部点集合的距离,并返回是否存在增广路。使用 dfs 函数进行深度优先搜索,查找增广路并更新匹配关系。最后,使用 hopcroft_karp 函数实现 Hopcroft-Karp 算法,并返回最大匹配的边数。

代码:

#include <bits/stdc++.h>
using namespace std;

const int INF = 1e9;
const int MAXN = 1e5 + 5;

int n, m, e;
int match[MAXN], dist[MAXN];
vector<int> graph[MAXN];

bool bfs()
{
    queue<int> q;
    for (int i = 1; i <= n; ++i)
    {
        if (match[i] == 0)
        {
            dist[i] = 0;
            q.push(i);
        }
        else
        {
            dist[i] = INF;
        }
    }

    dist[0] = INF;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();

        if (u == 0)
        {
            continue;
        }

        for (int v : graph[u])
        {
            if (dist[match[v]] == INF)
            {
                dist[match[v]] = dist[u] + 1;
                q.push(match[v]);
            }
        }
    }

    return dist[0] != INF;
}

bool dfs(int u)
{
    if (u == 0)
    {
        return true;
    }

    for (int v : graph[u])
    {
        if (dist[match[v]] == dist[u] + 1 && dfs(match[v]))
        {
            match[u] = v;
            match[v] = u;
            return true;
        }
    }

    dist[u] = INF;
    return false;
}

int hopcroft_karp()
{
    int ans = 0;
    fill(match, match + n + m + 1, 0);

    while (bfs())
    {
        for (int i = 1; i <= n; ++i)
        {
            if (match[i] == 0 && dfs(i))
            {
                ++ans;
            }
        }
    }

    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> m >> e;

    for (int i = 1; i <= e; ++i)
    {
        int u, v;
        cin >> u >> v;
        graph[u].push_back(v + n);
        graph[v + n].push_back(u);
    }

    cout << hopcroft_karp() << '\n';

    return 0;
}

3、

题目:P1129 [ZJOI2007] 矩阵游戏

小 Q 是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个 n×n 黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:

  • 行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)。
  • 列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色)。

游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。

对于某些关卡,小 Q 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!于是小 Q 决定写一个程序来判断这些关卡是否有解。

输入:第一行包含一个整数 T,表示数据的组数,对于每组数据,输入格式如下:第一行为一个整数,代表方阵的大小 n。 接下来 n 行,每行 n 个非零即一的整数,代表该方阵。其中 0 表示白色,1 表示黑色。

输出:对于每组数据,输出一行一个字符串,若关卡有解则输出 Yes,否则输出 No

思路:

将给定的方阵拆分成两个顶点集合,分别表示行和列,根据方阵中每个格子的颜色(0 或 1),在行顶点和列顶点之间建立边。如果格子是黑色(值为 1),则在相应的行顶点和列顶点之间连一条边,使用最大匹配算法来找到二分图的最大匹配数。最大匹配数表示可以匹配的最大顶点对数。如果最大匹配数等于方阵的大小,说明存在一种方案可以使得主对角线上的格子均为黑色,如果最大匹配数等于方阵的大小,返回 "Yes";否则返回 "No"。

代码:

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1005; // 最大顶点数量

vector<int> adj[MAXN]; // 邻接表存储边
int match[MAXN];       // 匹配数组,match[i] 表示与 i 进行匹配的顶点编号
bool visited[MAXN];    // 是否访问过

// 深度优先搜索找增广路径
bool dfs(int u) {
    for (int v : adj[u]) {
        if (!visited[v]) {
            visited[v] = true;
            if (match[v] == -1 || dfs(match[v])) {
                match[u] = v;
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

// 最大匹配算法
int maxMatching(int n) {
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        match[i] = -1;
    }
    for (int i = 1; i <= n; i++) {
        if (match[i] == -1) {
            fill(visited, visited + n + 1, false);
            if (dfs(i)) {
                cnt++;
            }
        }
    }
    return cnt;
}

// 判断是否有解
string solve(vector<vector<int>>& matrix, int n) {
    for (int i = 1; i <= 2 * n; i++) {
        adj[i].clear();
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == 1) {
                adj[i+1].push_back(j+n+1);
                adj[j+n+1].push_back(i+1);
            }
        }
    }
    int maxMatch = maxMatching(2 * n);
    if (maxMatch == n) {
        return "Yes";
    } else {
        return "No";
    }
}

int main() {
    int T;
    cin >> T;

    while (T--) {
        int n;
        cin >> n;
        vector<vector<int>> matrix(n, vector<int>(n));

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> matrix[i][j];
            }
        }

        cout << solve(matrix, n) << endl;
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值