SPOJ 839 Optimal Marks 学习简记

这道题目是《最小割模型在信息学竞赛中的应用》中的例题

①异或拆成一位一位的这个之前做过题

②学习了:”在给定的图中求完最大流后,如何求出S-T割中,两个点集分别有哪些点"

直接在跑过最大流后的图上,用cap != 0 的边做搜索就可以了 

一个最大流可能对应好几个割, 要求出不同的割可以通过不同的建图方式来实现

③个人看法:

在做这个题目之前呢

最小割模型感觉上比最大流模型抽象很多,最大流模型很容易想出一个具体的“半现实”的问题

最小割模型就很迷,除了题目指明要求最小割的大小,一下子想不到什么对应的现实问题

论文作者在分析这个题目的时候顺带一定程度上的解决了我这个问题

“点分两类;异类间的关系计入目标函数;优化目标是最小化”

这样的问题就可以用最小割模型解决



以后遇到一定要想到啊

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cctype>
#include <cmath>
#include <vector>
#include <sstream>
#include <bitset>
#include <deque>
#include <iomanip>
using namespace std;
#define pr(x) cout << "x = " << x << endl;
#define bug cout << "bugbug" << endl;

typedef long long ll;
typedef pair<int, int> P;
typedef unsigned int uint;
const int MOD = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 5e2 + 4;
const int maxm = 3e3 + 4;
struct Edge{
    int to, cab, rev;
    Edge(int _to, int _cap, int _rev):to(_to), cab(_cap), rev(_rev){

    }
};
vector<Edge> G[maxn];
int level[maxn], iter[maxn], n, m, u[maxm], v[maxm], cab, K, maxv;
ll num[maxn], pos[maxn], val[maxn];
bool tag[maxn];
inline void add(int u, int v, int cap){
    G[u].push_back(Edge(v, cap, G[v].size()));
    G[v].push_back(Edge(u, 0, G[u].size() - 1));
    return;
} 
void bfs(int s, int t){
    level[s] = 0;
    queue<int> q;
    q.push(s);
    while(q.size()){
        int u = q.front(); q.pop();
        for (int i = 0; i < G[u].size(); ++i){
            int to = G[u][i].to;
            if (level[to] == -1 && G[u][i].cab > 0){
                level[to] = level[u] + 1;
                q.push(to);
            }
        }
    }
    return;
}
int dfs(int u, int t, int f){
    if (u == t) return f;
    for (int& i = iter[u]; i < G[u].size(); ++i){
        int to = G[u][i].to;
        if (level[to] > level[u] && G[u][i].cab > 0){
            int d = dfs(to, t, min(f, G[u][i].cab));
            if (d > 0){
                G[u][i].cab -= d;
                G[to][G[u][i].rev].cab += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t){
    int ret = 0, tmp;
    while(true){
        memset(level, -1, sizeof level);
        bfs(s, t);
        if (level[t] == -1) return ret;
        memset(iter, 0, sizeof iter);
        while((tmp = dfs(s, t, inf)) != 0) ret += tmp;
    }
}
bool vis[maxn];
void DFS(int u, ll j){
	vis[u] = true;
	for (int i = 0; i < G[u].size(); ++i){
		int to = G[u][i].to;
		if (vis[to] || G[u][i].cab == 0) continue;
		num[to] |= 1 << j;
		DFS(to, j); 
	}
	return;
}
void Solve(){
	int i, j, k;
	for (i = 0; i <= maxv; ++i){
		for (j = 0; j <= n + 1 + 1; ++j) G[j].clear();
		for (j = 1; j <= K; ++j){
			if (val[j] >> i & 1) add(0, pos[j], inf);
			else add(pos[j], n+1, inf);
		}
		for (j = 1; j <= m; ++j){
			add(u[j], v[j], 1);
			add(v[j], u[j], 1);
		}
		max_flow(0, n+1);
		memset(vis, false, sizeof vis);
		DFS(0, i);
	}
	return;
}
int main(){
//必须编译过才能交 
//    ios::sync_with_stdio(false);
    int ik, i, j, k, kase;
    scanf("%d", &kase);
    while(kase--){
    	scanf("%d%d", &n, &m);
    	for (i = 1; i <= m; ++i) scanf("%d%d", &u[i], &v[i]);
    	memset(tag, false, sizeof tag);
    	memset(num, 0, sizeof num);
    	scanf("%d", &K);
    	maxv = -1;
    	for (i = 1; i <= K; ++i){
			scanf("%lld%lld", &pos[i], &val[i]);
			tag[pos[i]] = true;
			num[pos[i]] = val[i];
			for (j = maxv + 1; j < 33; ++j)
				if (val[i] >> j & 1) maxv = j;
		}
//		pr(maxv);
		Solve();
		for (i = 1; i <= n; ++i) printf("%lld\n", num[i]);
	}
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值