算法刷题记录(Day 9)

Going Home(poj 2195)

原题链接
题目类型:带权值的二分图的完美匹配问题

使用KM算法。由于KM算法是用于求最大权值的问题,因此我们需要将权值取为负数。

#include<iostream>
#include<vector>
using namespace std;
#define NMMAX 110
#define INF 0x3f3f3f3f
//KM算法所需要的变量
int map[NMMAX][NMMAX];
int Lx[NMMAX], Ly[NMMAX];//顶标
int X[NMMAX], Y[NMMAX];//代表是否已经访问过
int G[NMMAX];//代表的是当前已经为Y匹配到了X中的哪一个元素
int N,C;

vector<pair<int, int> > man;
vector<pair<int, int> > house;

void update() {
	int inc = INF;
	//为了给冲突的X集合中的点添加边,应该从Y中非冲突的点中找
	//需要确定最小的步长,以达到最后的收益最大的情况
	for (int i = 1; i <= N; i++) {
		if (X[i]) {
			for (int j = 1; j <= N; j++) {
				if (!Y[j]) inc = min(inc, Lx[i] + Ly[j] - map[i][j]);
			}
		}
	}

	//为了是本来就存在的边依然存在,X中的点减去inc,Y中的点加上inc
	for (int i = 1; i <= N; i++) {
		if (X[i]) Lx[i] -= inc;
	}
	for (int j = 1; j <= N; j++) {
		if (Y[j]) Ly[j] += inc;
	}
}

int find(int x) {
	X[x] = 1;
	//遍历Y集中的点
	for (int j = 1; j <= N; j++) {
		if (Lx[x] + Ly[j] == map[x][j] && !Y[j]) {
			Y[j] = 1;
			if (!G[j] || find(G[j])) {
				G[j] = x;
				return 1;
			}
		}
	}
	return 0;
}
int KM() {
	//初始化顶标和匹配数组
	int res = 0;//代表的是匹配数
	memset(G, 0, sizeof(G));
	for (int i = 1; i <= N; i++) {
		Lx[i] = -INF;
		for (int j = 1; j <= N; j++) {
			Ly[j] = 0;
			Lx[i] = max(Lx[i], map[i][j]);
		}
	}
	for(int i = 1; i <= N; i++){//为X中的元素寻找匹配
		while (1) {
			//注意:这里没进行一次查找都要清空!!!
			memset(X, 0, sizeof(X));
			memset(Y, 0, sizeof(Y));
			if (find(i)) {
				res++;
				break;
			}

			//没有给i找到匹配,修改顶标,并更新
			update();
		}
	}

	if (res != N) cout << "NO MATCH!" << endl;
	else {
		int ret = 0;
		for (int i = 1; i <= N; i++) ret += (Lx[i]+Ly[i]);
		return ret;
	}
	return 0;
}
int main() {
	while (1) {
		int n, m;
		cin >> n >> m;
		if (!n && !m) break;
		man.clear();
		house.clear();
		for (int i = 0; i < n; i++) {
			getchar();
			for (int j = 0; j < m; j++) {
				char c = getchar();
				if (c == 'm') man.push_back(make_pair(i, j));
				if (c == 'H') house.push_back(make_pair(i, j));
			}
		}
		N = man.size();
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				map[i + 1][j + 1] = -abs(man[i].first - house[j].first) - abs(man[i].second - house[j].second);
				//cout << map[i + 1][j + 1];
			}
			//cout << endl;
		}
		cout << -KM() << endl;
	}
}

debug过程:
1.map[i + 1][j + 1] = -abs(man[i].first - house[j].first) - abs(man[i].second - house[j].second);中的j误写成了i导致了结果的错误
2.KM算法中的标记位在为每一个X集合中的元素的每一次匹配寻找过程都必须重置,否则会导致TL问题 在这里插入图片描述

Power Network(poj 2459)

原题链接

dinic算法
dinic算法之解释cur数组的内涵

解题思路:设置一个源点,其连接到各个power station,边值为每个供电站的供电最大值;设置一个汇点,连接各个consumer,边值为每个消费节点的消耗最大值。而其余各个点之间的连线的流量最大值直接从题目中获得。

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

#define maxn 150 // count of Node
#define INF 0x3f3f3f3f
int n, np, nc, m, s, t,cm;//n代表的是点数,m代表的是总的边数,cm代表的是当前的边数
vector<int> G[maxn]; //G是链式前向星的结构
int d[maxn], cur[maxn]; //d存储的是每次BFS得到的层数,cur存储的是当前已经遍历到的位置
bool vis[maxn];
struct Edge {
    int from, to, cap, flow;
    Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
vector<Edge> edges;



    void init(int n) {
        for (int i = 0; i < n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        cm = edges.size();
        G[from].push_back(cm - 2);
        G[to].push_back(cm - 1);
    }

    bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        d[s] = 0;
        vis[s] = 1;
        while (!Q.empty()) {
            int x = Q.front();
            Q.pop();
            for (int i = 0; i < G[x].size(); i++) {
                Edge& e = edges[G[x][i]];
                if (!vis[e.to] && e.cap > e.flow) {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }


    int DFS(int x, int a) {
        if (x == t || a == 0) return a;
        int flow = 0;
        int f;
        for (int& i = cur[x]; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) {
                e.flow += f;
                edges[G[x][i] ^ 1].flow -= f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        return flow;
    }

        int Maxflow(int s, int t) {
        int flow = 0;
        while (BFS()) {
            memset(cur, 0, sizeof(cur));
            flow += DFS(s, INF);
        }
        return flow;
    }

int main() {
    while (cin >> n >> np >> nc >> m) {
        //定义源点和汇点
       s = n;
        t = n + 1;
        n = n + 2;
       init(n);
        for (int i = 0; i < m; i++) {
            int cs, ct, cf;
            while (getchar() != '(');
            //cin >> cs >> ct >> cf; 注意不能这样来读
            scanf_s("%d,%d)%d", &cs, &ct, &cf);
            AddEdge(cs, ct, cf);
        }
        for (int i = 0; i < np; i++) {
            int ct, cf;
            // cin >> ct >> cf;
            while (getchar() != '(');
            scanf_s("%d)%d",&ct, &cf);
            AddEdge(s, ct, cf);
            m++;
        }
        for (int i = 0; i < nc; i++) {
            int cs, cf;
           // cin >> cs >> cf;
            while (getchar() != '(');
            scanf_s("%d)%d", &cs, &cf);//这里的格式化字符串使用错误
            AddEdge(cs, t, cf);
            m++;
        }
        cout << Maxflow(s,t) << endl;
    }
    
}

debug过程:
1.由于题目给出的格式较为复杂,因此进行关于边的读入的时候不能过于简单化,对于 (a,b)c 直接使用cin>>a>>b>>c是错误的,必须首先使用getchar将(以前的字符全部读取,然后使用scanf(“%d,%d)%d",&a,&b&c)才能正确地进行读入。

tip: 对于没有终结符的输入,可以使用while(cin>>a),当到达文件尾的时候,会自动停止。

总结

需要做一个总的关于图论的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值