1、hdu4411
题意:
有N+1个点,每个点与编号大于自己的点之间有一条有权边(权重通过floyd求得),现有k个人位于0处,要从k个人中选出若干个人遍历其它点并最终回到0点,使每个点(除0外)都被访问恰好一次,问最小费用之和为多少。
题解:
每个点至多走一次,显然需要把一个点拆成两个,一个出点一个入点之间费用为0流量为1,超级源点拆为流量为k费用为距离的边,由于原图无环,所以可以将i和i‘之间的费用设为-M,流量设为1,M应该大于源点和汇点间最长链的长度。由于每次都找最短路径,因此这些边一定会被有限考虑,因此可以保证这些边恰好走了一次。为了保证不走增广后产生的负权反向边,源点和汇点之间连一条流量为k费用为0的点。
代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 1010
#define maxm 20010
const int inf = 0x3f3f3f3f;
struct Nod {
int b, nxt;
int cap, cst;
void init(int b, int nxt, int cap, int cst) {
this->b = b;
this->nxt = nxt;
this->cap = cap;
this->cst = cst;
}
};
struct MinCost {
int E[maxn];
int n;
Nod buf[maxm * 2];
int len;
int p[maxn];
void init(int n) {
this->n = n;
memset(E, 255, sizeof(E));
len = 0;
}
void addCap(int a, int b, int cap, int cst) {
// printf("%d %d %d\n",a,b,cst);
buf[len].init(b, E[a], cap, cst);
E[a] = len++;
buf[len].init(a, E[b], 0, -cst);
E[b] = len++;
}
bool spfa(int source, int sink) {
static queue q;
static int d[maxn];
memset(d, 63, sizeof(d));
memset(p, 255, sizeof(p));
d[source] = 0;
q.push(source);
int u, v;
while (!q.empty()) {
u = q.front();
q.pop();
for (int i = E[u]; i != -1; i = buf[i].nxt) {
v = buf[i].b;
if (buf[i].cap > 0 && d[u] + buf[i].cst < d[v]) {
d[v] = d[u] + buf[i].cst;
p[v] = i;
q.push(v);
}
}
}
return d[sink] != inf;
}
int solve(int source, int sink) {
int minCost = 0, maxFlow = 0;//需要maxFlow的话,想办法返回
while (spfa(source, sink)) {
int neck = inf;
for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b])//buf[t^壹].b是父节点
neck = min(neck, buf[t].cap);
maxFlow += neck;
for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b]) {
buf[t].cap -= neck;
//printf("%d\n",buf[t].b);
buf[t ^ 1].cap += neck;
minCost += buf[t].cst * neck;
}
//printf("-----\n");
}
return minCost;
}
} mc;
int map[110][110],n;
void floyd() {
for (int k =0; k <= n; k++)
for (int i =0; i <= n; i++)
for (int j=0; j <= n; j++){
if (map[i][k] + map[k][j] < map[i][j])
map[i][j] = map[i][k]+map[k][j];
}
}
int main() {
int m, k, a, b, c;
while (scanf("%d%d%d",&n,&m,&k)&&n) {
for (int i = 0; i <= n; i++)
for (int j = 0;j<= n; j++)
map[i][j] = inf;
while (m--) {
scanf("%d%d%d", &a, &b, &c);
map[b][a]=map[a][b] = min(map[a][b],c);
}
floyd();
mc.init(n * 2 + 3);
mc.addCap(0, n * 2 + 1, k, 0);
mc.addCap(n*2+1,n*2+2,k,0);
int temp=1<<23;
for (int i = 1; i <= n; i++) {
mc.addCap(i, i + n,1,-temp);
mc.addCap(n*2+1,i,1,map[i][0]);
mc.addCap(i+n, n*2+2,1,map[i][0]);
for (int j = i+1;j<=n;j++)
mc.addCap(i+n,j,1,map[i][j]);
}
printf("%d\n",mc.solve(0,n*2+2)+temp*n);
}
return 0;
}
2、hdu4971
题意:
n(n <= 20)个项目,m(m <= 50)个技术问题,做完一个项目可以有收益profit (<= 1000),做完一个项目必须解决相应的技术问题,解决一个技术问题需要付出cost ( <= 1000),技术问题之间有先后依赖关系,求最大收益。
题解:
每个点有权值,点之间存在依赖关系,所以是最大权闭合图。对于最大权闭合图我们的解决方法一般是,从S向所有点权为正的点连边,边权为点权,从所有点权为负的点向T连边,边权为点权的绝对值,原图中的边量为inf。
然后我们花几个图可以发现 所有点权为正的点权和-最小割=最大权,然后转换为最大流问题。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
3、poj1087
题意:
一堆插头,一堆用电器,一堆插座,一堆转换器,问你最多能满足几个。
题解:
虽然是网络流专题里的,我用二分图匹配做的。用电器一个集合,插座一个集合,转换器负责连边,因为可能存在鬼畜的转换器,所以floyd传递一下闭包,然后跑二分图最大匹配。
网上的网络流题解那个反向边连法我没看懂。
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
4、zoj2314
题意:
给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流淌物质。并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li。
题解:
令每一个点流进来的流=流出去的流,对于每一个点i,令Mi= sum(i点所有流进来的下界流)– sum(i点所有流出去的下界流)
如果Mi大于0,代表此点必须还要流出去Mi的自由流,那么我们从源点连一条Mi的边到该点。
如果Mi小于0,代表此点必须还要流进来Mi的自由流,那么我们从该点连一条Mi的边到汇点。
如果求S->T的最大流,看是否满流(S的相邻边都流满)。满流则有解,否则无解
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
5、nefu500
题意:中文题。
题解:
题目问你最大值最小,显然是二分,二分边权最大值,如果小于就连边,跑最大流,如果慢流就说明可以。
代码:
玛德re是以为你cnt每次二分忘记初始化了,WA是因没有保留那个最小的cnt1。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include