网络流
目录
这次的题目没有写完,虽然题目很少吧……以后机会再补上
A ACM Computer Factory(POJ 3436)
B Dining(POJ 3281)
题目大意:
有n头牛,f种食物,d种饮料。每头牛有自己喜欢的食物和饮料。输出能让牛吃到自己喜欢的食物和喝到自己喜欢饮料的数量。
思路:
最大流。
建图方面:
上图。其中容量都是1。然后直接套dinic就行了。
AC代码
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int max_n = 400 + 100;
int res[max_n][max_n];
int dis[max_n];
int source, sink, n, maxflow;
void add(int from, int to, int cap)
{
res[from][to] = cap;
}
int bfs()
{
int k;
queue<int> q;
memset(dis, -1, sizeof(dis));
dis[sink] = 0;
q.push(sink);
while (!q.empty())
{
k = q.front();
q.pop();
for (int i = 0; i < n; i++)
{
if (dis[i] == -1 && res[i][k])
{
dis[i] = dis[k] + 1;
q.push(i);
}
}
if (k == source)
return 1;
}
return 0;
}
int dfs(int cur, int cp)
{
if (cur == sink)
return cp;
int tmp = cp, t;
for (int i = 0; i < n && tmp; i++)
{
if (dis[i] + 1 == dis[cur] && res[cur][i])
{
t = dfs(i, min(res[cur][i], tmp));
res[cur][i] -= t;
res[i][cur] += t;
tmp -= t;
}
}
return cp - tmp;
}
void dinic()
{
maxflow = 0;
while (bfs())
maxflow += dfs(source, 1 << 30);
}
int main()
{
int f, d;
int ff, dd, tmp;
cin >> n >> f >> d;
//源点 汇点
source = 0, sink = 2 * n + f + d + 1;
//源点到食物
for (int i = 1; i <= f; i++)
add(source, 2 * n + i, 1);
//饮料到汇点
for (int i = 1; i <= d; i++)
add(2 * n + f + i, sink, 1);
//食物牛到饮料牛
for (int i = 1; i <= n; i++)
add(i, n + i, 1);
for (int i = 1; i <= n; i++)
{
cin >> ff >> dd;
for (int j = 1; j <= ff; j++)
{
//食物到食物牛
cin >> tmp;
add(2 * n + tmp, i, 1);
}
for (int j = 1; j <= dd; j++)
{
//饮料牛到饮料
cin >> tmp;
add(i + n, 2 * n + f + tmp, 1);
}
}
n = sink + 1; //代表总的点数了
dinic();
cout << maxflow << endl;
return 0;
}
C A Plug for UNIX(POJ 1087)
题目大意:
有一些插座,还有一些设备的插头,要给设备充电,还会给出某些插座可以互相插,问最少有多少个不被充。
思路:
就是求最多有多少个被充的,然后拿总数去减就行了。
建图方面:
1、插座到汇点的容量为 1
2、源点到设备的容量为 1
3、设备到插座的容量为 1
4、插座到插座的容量为 INF
使用map去存字符串,然后使用index下标讲所有字符串都转换成数字。然后直接跑dinic就行了。
AC代码
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
const int max_n = 800 + 100;
int res[max_n][max_n];
int dis[max_n];
int source, sink, n, maxflow;
void add(int from, int to, int cap)
{
res[from][to] = cap;
}
int bfs()
{
int k;
queue<int> q;
memset(dis, -1, sizeof(dis));
dis[sink] = 0;
q.push(sink);
while (!q.empty())
{
k = q.front();
q.pop();
for (int i = 0; i < n; i++)
{
if (dis[i] == -1 && res[i][k])
{
dis[i] = dis[k] + 1;
q.push(i);
}
}
if (k == source)
return 1;
}
return 0;
}
int dfs(int cur, int cp)
{
if (cur == sink)
return cp;
int tmp = cp, t;
for (int i = 0; i < n && tmp; i++)
{
if (dis[i] + 1 == dis[cur] && res[cur][i])
{
t = dfs(i, min(res[cur][i], tmp));
res[cur][i] -= t;
res[i][cur] += t;
tmp -= t;
}
}
return cp - tmp;
}
void dinic()
{
maxflow = 0;
while (bfs())
maxflow += dfs(source, 1 << 30);
}
map<string, int> mp; //字母标号
int main()
{
cin >> n;
source = 0, sink = max_n - 1;
int index = 1;
//插座到汇点
for (int i = 0; i < n; i++)
{
string ch;
cin >> ch;
mp[ch] = index++;
add(mp[ch], sink, 1);
}
int f;
cin >> f;
for (int i = 0; i < f; i++)
{
string s, ch;
cin >> s >> ch;
if (!mp[s])
mp[s] = index++;
//源点到设备
add(source, mp[s], 1);
if (!mp[ch])
mp[ch] = index++;
// 设备到插座
add(mp[s], mp[ch], 1);
}
int m;
cin >> m;
//插座到插座
for (int i = 0; i < m; i++)
{
string a, b;
cin >> a >> b;
if (!mp[a])
mp[a] = index++;
if (!mp[b])
mp[b] = index++;
add(mp[a], mp[b], 1 << 30);
}
n = max_n;
dinic();
cout << f - maxflow << endl;
return 0;
}
D Going Home(POJ 2195)
题目大意:
有一个地图,m是人,h是家,人要回家,一个家只能待一个人,一个人动一步要一块钱,问最小费用。
思路:
最小费用流。
建图方面:
1、找出所有人的坐标和所有家的坐标。
2、对于每一个家计算出所有人到这个家的价格(曼哈顿距离)当作费用,容量为1
3、将所有家连入源点,费用为0,容量为1
4、将所有的人连入汇点,费用为0,容量为1
建好图之后直接跑最小费用流算法就行了。
AC代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define endl "\n"
const int max_n = 1e4 + 20;
typedef pair<int, int> P;
struct edge
{
//终点 容量 费用 反向边
int to, cap, cost, rev;
};
int V;
vector<edge> G[max_n];
int h[max_n];
int dist[max_n];
int prevv[max_n], preve[max_n];
void add(int from, int to, int cap, int cost)
{
G[from].push_back((edge){to, cap, cost, (int)G[to].size()});
G[to].push_back((edge){from, 0, -cost, (int)G[from].size() - 1});
}
int min_cost_flow(int s, int t)
{
int res = 0;
fill(h, h + V, 0);
while (true)
{
priority_queue<P, vector<P>, greater<P>> que;
fill(dist, dist + V, INF);
dist[s] = 0;
que.push(P(0, s));
while (!que.empty())
{
P p = que.top();
que.pop();
int v = p.second;
if (dist[v] < p.first)
continue;
for (int i = 0; i < G[v].size(); i++)
{
edge &e = G[v][i];
if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
{
dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
prevv[e.to] = v;
preve[e.to] = i;
que.push(P(dist[e.to], e.to));
}
}
}
if (dist[t] == INF)
break;
for (int v = 0; v < V; v++)
h[v] += dist[v];
int d = INF;
for (int v = t; v != s; v = prevv[v])
{
d = min(d, G[prevv[v]][preve[v]].cap);
}
if (d == 0)
break;
res += d * h[t];
for (int v = t; v != s; v = prevv[v])
{
edge &e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
}
string s[max_n];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
while (cin >> n >> m)
{
if (n == 0 && m == 0)
break;
for (int i = 0; i < n; i++)
cin >> s[i];
vector<P> hh, mm;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (s[i][j] == 'H')
hh.push_back(make_pair(i, j));
if (s[i][j] == 'm')
mm.push_back(make_pair(i, j));
}
}
V = 2 * hh.size() + 2;
int s = 2 * hh.size();
int t = s + 1;
for (int i = 0; i < V + 10; i++)
G[i].clear();
for (int i = 0; i < hh.size(); i++)
{
for (int j = 0; j < hh.size(); j++)
add(i, hh.size() + j, 1, abs(mm[i].first - hh[j].first) + abs(mm[i].second - hh[j].second));
}
for (int i = 0; i < hh.size(); i++)
{
add(s, i, 1, 0);
add(hh.size() + i, t, 1, 0);
}
cout << min_cost_flow(s, t) << endl;
}
return 0;
}
E Minimum Cost(POJ 2516)
F Power Network(POJ 1459)
总结
网络流部分,感觉建图是最难的,虽然很多题没有写出来,主要是因为题目读完了,算法也知道用啥了,但是完全不知道怎么建图,,,感觉建好图,这个题目就可以解决80%了。
剩下的几道题目,以后对网络流的理解更深了之后再去写吧,现在也就只会套板子去做,感觉很多细节方面都不知道怎么整的。