图论例题合集(三)

目录

 

A:LightOJ - 1156 Jumping Frogs

B:LightOJ - 1167 Dangerous Tunnels

C:LightOJ - 1176 Getting a T-shirt

D:LightOJ - 1177 Angry Programmer

E:LightOJ - 1405 The Great Escape

F:LightOJ - 1071 Baker Vai

G:LightOJ - 1222 Gift Packing

H:LightOJ - 1237 Cyber Cafe


A:LightOJ - 1156 Jumping Frogs:题意:给定一段宽为m的河道,在河道中有n个石头排成一条直线,石头分两种:B种石头可以踩无限多次,S种石头只能踩一次。现在要在河道中来回一次,也就是过河两次,求单次所走最大距离的最小值 思路:之前做过类似的题目,不过这题更麻烦一点。因为S种石头只能踩一次,所以要拆点连边,容量为1,为了方便,对B种石头也顺便拆点,容量大于1即可(但不要太大,会发生悲剧-_-||),然后二分枚举最大距离,小于等于枚举值的边就连上,容量大于1,源点汇点分别为河的左右边,注意源点汇点距离小于等于枚举值时,也要连上,此时最大流的意义就是通过河道有几条通路,大于等于2时说明枚举值满足题目条件,接下不断枚举,直到找到最小的满足条件的值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
 
const int N = 210;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct edge
{
    int to, cap, next;
}g[N*N*2];
int head[N], iter[N], level[N];
int n, m, cnt, _case = 0;
void add_edge(int v, int u, int cap)
{
    g[cnt].to = u, g[cnt].cap = cap, g[cnt].next = head[v], head[v] = cnt++;
    g[cnt].to = v, g[cnt].cap = 0, g[cnt].next = head[u], head[u] = cnt++;
}
bool bfs(int s, int t)
{
    memset(level, -1, sizeof level);
    level[s] = 0;
    queue<int> que;
    que.push(s);
    while(! que.empty())
    {
        int v = que.front(); que.pop();
        for(int i = head[v]; i != -1; i = g[i].next)
        {
            int u = g[i].to;
            if(g[i].cap > 0 && level[u] < 0)
            {
                level[u] = level[v] + 1;
                que.push(u);
            }
        }
    }
    return level[t] == -1;
}
int dfs(int v, int t, int f)
{
    if(v == t) return f;
    for(int &i = iter[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(g[i].cap > 0 && level[v] < level[u])
        {
            int d = dfs(u, t, min(g[i].cap, f));
            if(d > 0)
            {
                g[i].cap -= d, g[i^1].cap += d;
                return d;
            }
        }
    }
    return 0;
}
int dinic(int s, int t)
{
    int flow = 0, f;
    while(true)
    {
        if(bfs(s, t)) return flow;
        memcpy(iter, head, sizeof head);
        while(f = dfs(s, t, INF),f > 0)
            flow += f;
    }
}
int main()
{
    int t;
    char ch[N];
    int d[N];
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
            scanf(" %c-%d", &ch[i], &d[i]);
        int l = 0, r = m, res = 0;
        while(l <= r)
        {
            cnt = 0;
            memset(head, -1, sizeof head);
            int mid = (l + r) >> 1;
            for(int i = 1; i <= n; i++) //石头拆点连边
                if(ch[i] == 'S') add_edge(i, n + i, 1);
                else add_edge(i, n + i, 2);
            for(int i = 1; i <= n; i++) //对于石头,连接源点和汇点
            {
                if(abs(m - d[i]) <= mid)
                    add_edge(i + n, 2 * n + 1, 2);
                if(abs(d[i] - 0) <= mid)
                    add_edge(0, i, 2);
            }
            if(m - 0 <= mid) add_edge(0, 2 * n + 1, 2); //源点和汇点连边
            for(int i = 1; i <= n; i++) //石头之间连边
                for(int j = i + 1; j <= n; j++)
                    if(abs(d[i] - d[j]) <= mid)
                    {
                        add_edge(i + n, j, 2);
                        //add_edge(j + n, i, 2);
                    }
            if(dinic(0, 2 * n + 1) >= 2) r = mid - 1, res = mid;
            else l = mid + 1;
        }
        printf("Case %d: %d\n", ++_case, l);
    }
    return 0;
}

B:LightOJ - 1167 Dangerous Tunnels:题意:从0--n+1,中间有一些路,走的时候起点必须大于终点,每条路都有一定的危险值,每一种方案需要挑选至少K条路,这些路里危险度最大的那条路就是整个方案的危险值,输出最小的危险值,如果没有方案的话输出no solutation! 二分枚举危险度,建图,然后跑一边拆点最大流,每条边容量为1,判断最大流结果是否大于等于k。

#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;
const int inf = ~0u >> 2;
const int N = 215;
const int M = 100000;
struct eg {
	int u, v, cap, cost;
	eg() {}
	eg(int a, int b, int c, int d) { u = a, v = b, cap = c, cost = d; }
}edg[M];
int fir[N], nex[M], ecnt;
void add(int a, int b, int c, int d) {
	edg[ecnt] = eg(a, b, c, d);
	nex[ecnt] = fir[a], fir[a] = ecnt++;
	edg[ecnt] = eg(b, a, 0, -d);
	nex[ecnt] = fir[b], fir[b] = ecnt++;
}
int lev[N];
bool Bfs(int s, int t, int up) {
	queue<int>q;
	memset(lev, -1, sizeof(lev));
	lev[s] = 0; q.push(s);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		if (t == u) return 1;
		for (int k = fir[u]; k != -1; k = nex[k]) {
			if (edg[k].cost > up) continue;
			int v = edg[k].v;
			if (edg[k].cap && lev[v] == -1) {
				lev[v] = lev[u] + 1;
				q.push(v);
			}
		}
	}
	return 0;
}
int Dfs(int s, int t, int low, int up) {
	if (s == t) return low;
	int res = 0, a;
	for (int k = fir[s]; k != -1; k = nex[k]) {
		if (edg[k].cost > up) continue;
		int v = edg[k].v;
		if (edg[k].cap && lev[v] == lev[s] + 1) {
			a = Dfs(v, t, min(low - res, edg[k].cap), up);
			edg[k].cap -= a;
			edg[k ^ 1].cap += a;
			res += a;
			if (res == low) return res;
		}
	}
	if (res == 0) lev[s] = -1;
	return res;
}
int Dinic(int s, int t, int up) {
	int res = 0, minflow;
	while (Bfs(s, t, up)) {
		while (minflow = Dfs(s, t, inf, up)) res += minflow;
	}
	//printf("maxflow = %d\n", res);
	return res;
}
int solve(int s, int t, int up) {
	int l = 0, r = inf, ans = -1;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (Dinic(s, t, mid) >= up) r = mid - 1, ans = mid;
		else l = mid + 1;
		for (int i = 0; i < ecnt; i += 2) {
			edg[i].cap += edg[i ^ 1].cap;
			edg[i ^ 1].cap = 0;
		}
	}
	return ans;
}
int main() {
	int T, ca = 1;
	scanf("%d", &T);
	while (T--) {
		memset(fir, -1, sizeof(fir)); ecnt = 0;
		int n, m;
		scanf("%d%d", &n, &m);
		int s = 0, t1 = n + 1, t2 = 2 * n + 2;
		for (int i = 1; i <= n; ++i) add(i, i + n + 1, 1, 0);
		for (int i = 1; i <= m; ++i) {
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			if (a > b) swap(a, b);
			else if (a == b) continue;
			if (a == 0) add(s, b, 1, c);
			else add(a + n + 1, b, 1, c);
		}
		int k;
		scanf("%d", &k);
		add(t1, t2, k, 0);
		int ans = solve(s, t2, k);
		if (ans == -1) printf("Case %d: no solution\n", ca++);
		else printf("Case %d: %d\n", ca++, ans);
	}
}

C:LightOJ - 1176 Getting a T-shirt:题目大意:有m个人,衣服有n种颜色,(感觉这个地方有点绕,其实是有6个尺码,每个尺码有n种颜色的衣服,而题目又说了竞赛者不关心衣服颜色,所以意思就是每个尺码有n件衣服),给出每个参赛者想要的两种尺码,问能不能满足所有参赛者的要求。 考虑网络流,源点与每个人连一条容量为1的边,每个人与他想要的尺码连一条容量为1的边,这就限制了每个人只能选一件衣服的条件,对于每个尺码,与汇点连一条容量为n的边,表示每个尺码有n件跑一遍最大流,只要最后跑出的答案等于参赛者的人数就是满足,否则不满足。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
using namespace std;
const int maxn=1010;
const int maxm=10010;
const int inf=0x3f3f3f3f;
struct Node
{
    int to;
    int capa;
    int next;
}edge[maxm];
int source,sink;
int cnt;
int head[maxn];
bool vis[maxn];
int num[maxn];
int dep[maxn];
map<string,int> ma;
void init()
{
    memset(head,-1,sizeof(head));
    ma["XS"]=1;
    ma["S"]=2;
    ma["M"]=3;
    ma["L"]=4;
    ma["XL"]=5;
    ma["XXL"]=6;
    cnt=0;
    return;
}
void add(int u,int v,int capa)
{
    edge[cnt].to=v;
    edge[cnt].capa=capa;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].capa=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
    return;
}
bool bfs()
{
    queue<int> que;
    que.push(source);
    memset(dep,-1,sizeof(dep));
    dep[source]=0;
    while(!que.empty())
    {
        int node=que.front();
        que.pop();
        for(int i=head[node];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].capa>0&&dep[v]==-1)
            {
                dep[v]=dep[node]+1;
                if(v==sink) return true;
                que.push(v);
            }
        }
    }
    return dep[sink]!=-1;
}
int dfs(int node,int minn)
{
    if(node==sink||minn==0)
    {
        return minn;
    }
    int r=0;
    for(int i=head[node];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(dep[v]==dep[node]+1&&edge[i].capa>0)
        {
            int tmp=dfs(v,min(edge[i].capa,minn));
            if(tmp>0)
            {
                edge[i].capa-=tmp;
                edge[i^1].capa+=tmp;
                r+=tmp;
                minn-=tmp;
                if(!minn) break;
            }
        }
    }
    if(!r) dep[node]=-1;
    return r;
}
int dinic()
{
    int maxflow=0;
    while(bfs())
    {
        maxflow+=dfs(source,inf);
    }
    return maxflow;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int test;
    scanf("%d",&test);
    for(int cas=1;cas<=test;cas++)
    {
        init();
        int n,m;
        scanf("%d%d",&n,&m);
        source=0;
        sink=m+10;
        for(int i=1;i<=m;i++)
        {
            string a,b;
            cin>>a>>b;
            add(source,i,1);
            add(i,ma[a]+m,1);
            add(i,ma[b]+m,1);
        }
        for(int i=1;i<=6;i++)
        {
            add(m+i,sink,n);
        }
        printf("Case %d: %s\n",cas,dinic()==m?"YES":"NO");
    }
    return 0;
}

D:LightOJ - 1177 Angry Programmer:题意:一共有n台计算机,1号是发送端,n号是接受端你想让发送端与接受端之间无法通信,已经破坏每台电脑所需的费用(2--n-1),共有m条导线,每条线连接着两条电脑,并告诉你破坏这条线所需的费用,问你最少花费多少可以使得发送端与接受端之间无法通信。 思路:最小割的问题,因为最小割等于最大流,我们只需要求最大流即可。把计算机看成是顶点,由于顶点上有费用的限制,所以我们进行拆点,每个点到自身的流量为破坏这个电脑的费用,源点(1)和汇点(n)到自身的流量设为INF。 (这里由于流量的范围比较大,用Ford-Fulkerson算法可能会超时,这里我们改用Dinic算法,实际测试8ms就可过) 总结:拆点连边时容量不能为0,求最小割时不想让某条边选入最小割中,就置为极大值。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue> 
#define INF 1<<31-1
#define min(x,y)(x<y?x:y)
using namespace std;
struct Edge
{
	int to;
	int cap;
	int rev;
};
vector<Edge>g[110];
int p[51];
int len[110];
int pos[110];
void add_edge(int from,int to,int cap)
{
	g[from].push_back((Edge){to,cap,g[to].size()});
	g[to].push_back((Edge){from,0,g[from].size()-1});
}
bool bfs(int s,int t)
{
	memset(len,-1,sizeof(len));
	queue<int>Q;
	Q.push(s);
	len[s]=0;
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		for(int i=0;i<g[u].size();i++)
		{
			Edge e=g[u][i];
			int v=e.to;
			if(e.cap>0&&len[v]==-1)
			{
				len[v]=len[u]+1;
				Q.push(v);
			}
		}
	}
	if(len[t]==-1)
		return false;
	return true;
}
int dfs(int u,int t,int f)
{
	if(u==t)
		return f;
	for(int &i=pos[u];i<g[u].size();i++)
	{
		Edge &e=g[u][i];
		int v=e.to;
		if(e.cap>0&&len[u]==len[v]-1)
		{
			int d=dfs(v,t,min(e.cap,f));
			if(d>0)
			{
				e.cap-=d;
				g[v][e.rev].cap+=d;
				return d;
			}
		}
	}
	return 0;
}
int max_flow(int s,int t)
{
	int res=0;
	while(bfs(s,t))
	{
		memset(pos,0,sizeof(pos));
		while(true)
		{
			int d=dfs(s,t,INF);
		    if(d<=0)
		    	break;
			res+=d; 
		}
	}
	return res;
}
int main()
{
	int c,n,m,i,from,to,cap,s,t,k=0;
	scanf("%d",&c);
	while(c--)
	{
		scanf("%d%d",&n,&m);
		s=1;t=2*n;
		for(i=s;i<=t;i++)
			g[i].clear();
		for(i=2;i<n;i++)
			scanf("%d",&p[i]);
		for(i=0;i<m;i++)
		{
			scanf("%d%d%d",&from,&to,&cap);
			add_edge(from+n,to,cap);              //无向图双向都加边即可 
			add_edge(to+n,from,cap);
		}
		for(i=2;i<n;i++)
		{
			add_edge(i,i+n,p[i]);
		}
		add_edge(s,s+n,INF);
		add_edge(n,t,INF);
		int res=max_flow(s,t);
		printf("Case %d: %d\n",++k,res);
	}
	return 0;
} 

E:LightOJ - 1405 The Great Escape:题目大意:给一个地图,地图上的*号表示人,人要逃出这个地图,'.'表示可以走的地方,每个人走的路径不能有重复的地方,问是否所有人都能逃出去。 对于每一个人,将它与源点连一条容量为1的边,对于每一个点,将其拆成两个点,连一条容量为1的边,表示只能走一次,对于那些在边界的点,将它与汇点相连,表示这个点可以出去,对于每一个点,将它与上下左右四个方向的点相连,表示能走。然后跑一遍最大流。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=2e4+100;
const int maxm=2000010;
const int inf=0x3f3f3f3f;
struct Node
{
    int to;
    int capa;
    int next;
}edge[maxm];
int n,m;
int cnt;
int nv;
int source,sink;
int head[maxn];
char map[110][110];
int dep[maxn];
int dirx[]={0,1,0,-1};
int diry[]={1,0,-1,0};
void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    return;
}
void add(int u,int v,int capa)
{
    edge[cnt].to=v;
    edge[cnt].capa=capa;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].capa=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
    return;
}
bool bfs()
{
    queue<int> que;
    que.push(source);
    memset(dep,-1,sizeof(dep));
    dep[source]=0;
    while(!que.empty())
    {
        int node=que.front();
        que.pop();
        for(int i=head[node];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].capa>0&&dep[v]==-1)
            {
                dep[v]=dep[node]+1;
                if(v==sink) return true;
                que.push(v);
            }
        }
    }
    return dep[sink]!=-1;
}
int dfs(int node,int minn)
{
    if(node==sink||minn==0)
    {
        return minn;
    }
    int r=0;
    for(int i=head[node];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(dep[v]==dep[node]+1&&edge[i].capa>0)
        {
            int tmp=dfs(v,min(edge[i].capa,minn));
            if(tmp>0)
            {
                edge[i].capa-=tmp;
                edge[i^1].capa+=tmp;
                r+=tmp;
                minn-=tmp;
                if(!minn) break;
            }
        }
    }
    if(!r) dep[node]=-1;
    return r;
}
int dinic()
{
    int maxflow=0;
    while(bfs())
    {
        maxflow+=dfs(source,inf);
    }
    return maxflow;
}
int getIndex(int x,int y)
{
    return (x-1)*m+y;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int test;
    scanf("%d",&test);
    for(int cas=1;cas<=test;cas++)
    {
        init();
        scanf("%d%d",&n,&m);
        source=0;
        sink=n*m*2+10;
        getchar();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",map[i]+1);
        }
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(i==1||i==n||j==1||j==m)
                {
                    add(getIndex(i,j)+n*m,sink,1);
                }
                add(getIndex(i,j),getIndex(i,j)+n*m,1);
                for(int k=0;k<4;k++)
                {
                    int x=i+dirx[k];
                    int y=j+diry[k];
                    if(x>=1&&x<=n&&y>=1&&y<=m)
                    {
                        add(getIndex(i,j)+n*m,getIndex(x,y),1);
                    }
                }
                if(map[i][j]=='*')
                {
                    sum++;
                    add(source,getIndex(i,j),1);
                }
            }
        }
        if(dinic()==sum) printf("Case %d: yes\n",cas);
        else printf("Case %d: no\n",cas);
    }
    return 0;
}

F:LightOJ - 1071 Baker Vai:题目大意:给一个n*m的矩阵,有一个人在左上角,要走到右下角,再从右下角走回左上角,走到右下角的过程中只能走右边和下边,走回左上角只能走左边和上边,要求路径不能重复,矩阵的每个格子有一个值 ,每次走到这个格子就能得到这个值,要求这个值最大 刚开始想着走回左上角要怎么办,其实很简单,它等同于让我们去找另外一条走到右下角的路径,想到这里就很简单了,我们将每个点拆点,起点和终点拆的点相连的边容量为2,其他的为1,因为起点终点会经过两遍,而其他点只能经过一遍,费用为权值取负,然后跑一遍费用流,但是这样得出的答案并不是正确答案,因为起点和终点都走了两遍,所以要减去一次起点和终点的权值。最大费用流时容量置为负。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=20010;
const int maxm=1e6+7;
const int inf=0x3f3f3f3f;
struct Node
{
	int to;
	int capa;
	int cost;
	int next;
}edge[maxm];
int cnt;
int n,m;
int source,sink;
int head[maxn];
int map[110][110];
int dis[maxn];
bool vis[maxn];
int pre[maxn];
int rec[maxn];
void init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
	return;
}
void add(int u,int v,int capa,int cost)
{
	edge[cnt].to=v;
	edge[cnt].capa=capa;
	edge[cnt].cost=cost;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].capa=0;
	edge[cnt].cost=-cost;
	edge[cnt].next=head[v];
	head[v]=cnt++;
	return;
}
int getIndex(int x,int y)
{
	return (x-1)*m+y;
}
bool spfa()
{
	queue<int> que;
	que.push(source);
	memset(dis,inf,sizeof(dis));
	memset(vis,false,sizeof(vis));
	memset(rec,-1,sizeof(rec));
	memset(pre,-1,sizeof(pre));
	dis[source]=0;
	vis[source]=true;
	while(!que.empty())
	{
		int node=que.front();
		que.pop();
		vis[node]=false;
		for(int i=head[node];~i;i=edge[i].next)
		{
			int v=edge[i].to;
			if(edge[i].capa>0&&dis[v]>dis[node]+edge[i].cost)
			{
				dis[v]=dis[node]+edge[i].cost;
				rec[v]=i;
				pre[v]=node;
				if(!vis[v])
				{
					vis[v]=true;
					que.push(v);
				}
			}
		}
	}
	return dis[sink]!=inf;
}
int mcmf()
{
	int mincost=0;
	while(spfa())
	{
		int node=sink;
		int flow=inf;
		while(node!=source)
		{
			flow=min(flow,edge[rec[node]].capa);
			node=pre[node];
		}
		node=sink;
		while(node!=source)
		{
			mincost+=flow*edge[rec[node]].cost;
			edge[rec[node]].capa-=flow;
			edge[rec[node]^1].capa+=flow;
			node=pre[node];
		}
	}
	return -mincost;
}
int main()
{
	//freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
	int test;
	scanf("%d",&test);
	for(int cas=1;cas<=test;cas++)
	{
		init();
		scanf("%d%d",&n,&m);
		source=1;
		sink=n*m*2;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				scanf("%d",&map[i][j]);
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				int x=getIndex(i,j);
				if((i==1&&j==1)||(i==n&&j==m))
				{
					add(x,x+n*m,2,-map[i][j]);
				}
				else
				{
					add(x,x+n*m,1,-map[i][j]);
				}
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				int x=getIndex(i,j);
				int down=getIndex(i+1,j);
				int right=getIndex(i,j+1);
				if(i<n) add(x+n*m,down,1,0);
				if(j<m) add(x+n*m,right,1,0);
			}
		}
		printf("Case %d: %d\n",cas,mcmf()-map[1][1]-map[n][m]);
	}
	return 0;
}

G:LightOJ - 1222 Gift Packing:题目大意:有n个礼物和n个盒子,每个盒子最多装一个礼物,每个礼物装进每个盒子都有不同的收益,问最大的收益 每个礼物和每个盒子建一个容量为1,费用为收益的负数的边,源点与每个礼物连一条容量为1,费用为0的边,每个盒子与汇点连一条容量为1,费用为0的边,跑一遍最小费用最大流即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<queue>
using namespace std;
const int maxn=1010;
const int maxm=10010;
const int inf=0x3f3f3f3f;
struct Node
{
    int to;
    int capa;
    int cost;
    int next;
}edge[maxm];
int source,sink;
int cnt;
int head[maxn];
bool vis[maxn];
int num[maxn];
int dep[maxn];
int pre[maxn];
int rec[maxn];
int dis[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    memset(pre,-1,sizeof(pre));
    memset(rec,-1,sizeof(rec));
    cnt=0;
    return;
}
void add(int u,int v,int capa,int cost)
{
    edge[cnt].to=v;
    edge[cnt].capa=capa;
    edge[cnt].cost=cost;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].capa=0;
    edge[cnt].cost=-cost;
    edge[cnt].next=head[v];
    head[v]=cnt++;
    return;
}
bool spfa()
{
    queue<int> que;
    que.push(source);
    memset(dis,inf,sizeof(dis));
    memset(vis,false,sizeof(vis));
    dis[source]=0;
    vis[source]=true;
    while(!que.empty())
    {
        int node=que.front();
        que.pop();
        vis[node]=false;
        for(int i=head[node];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].capa>0&&dis[v]>dis[node]+edge[i].cost)
            {
                dis[v]=dis[node]+edge[i].cost;
                rec[v]=i;
                pre[v]=node;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                }
            }
        }
    }
    return dis[sink]!=inf;
}
int mcmf()
{
    int maxflow=0;
    int mincost=0;
    while(spfa())
    {
        int node=sink;
        int flow=inf;
        while(node!=source)
        {
            flow=min(flow,edge[rec[node]].capa);
            node=pre[node];
        }
        node=sink;
        while(node!=source)
        {
            mincost+=flow*edge[rec[node]].cost;
            edge[rec[node]].capa-=flow;
            edge[rec[node]^1].capa+=flow;
            node=pre[node];
        }
    }
    return -mincost;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int test;
    scanf("%d",&test);
    for(int cas=1;cas<=test;cas++)
    {
        init();
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                int x;
                scanf("%d",&x);
                add(i,j+n,1,-x);
            }
        }
        source=0;
        sink=n*2+1;
        for(int i=1;i<=n;i++)
        {
            add(source,i,1,0);
        }
        for(int i=1;i<=n;i++)
        {
            add(i+n,sink,1,0);
        }
        printf("Case %d: %d\n",cas,mcmf());
    }
    return 0;
}

H:LightOJ - 1237 Cyber Cafe:题目大意:有n个人去咖啡店,老板将他们的进入时间和走的时间都记录下来了,但是弄混了,对于每个人来说,老板要收的钱是他在咖啡店待的时间T减去一个给出的常数K的差的平方,而且老板最多只收G元,超过G元也算G元,问最少收益和最大收益分别是多少 n^2建图,对于一个进入时间,找出符合的走的时间,连一条容量为1,费用为差值的平方的边,所有的进入时间与源点连一条容量为1,费用为0的边,所有的走的时间与汇点连一条容量为1,费用为0的边,跑一遍费用流,这时候得到的是最少费用。然后再重新建一次图,不同的是这一次进入时间与走的时间连的边费用取负数,然后也跑一遍费用流。如何判断impossible的情况,如果跑完费用流最后的总流量不等于n,就impossible。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=510;
const int maxm=100010;
const int inf=0x3f3f3f3f;
struct Node
{
    int to;
    int capa;
    int cost;
    int next;
}edge[maxm];
int n,K,G;
int source,sink;
int cnt;
int head[maxn];
int dis[maxn];
bool vis[maxn];
int dep[maxn];
int numa[maxn];
int numb[maxn];
int pre[maxn];
int rec[maxn];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    return;
}
void add(int u,int v,int capa,int cost)
{
    edge[cnt].to=v;
    edge[cnt].capa=capa;
    edge[cnt].cost=cost;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].to=u;
    edge[cnt].capa=0;
    edge[cnt].cost=-cost;
    edge[cnt].next=head[v];
    head[v]=cnt++;
    return;
}
bool spfa()
{
    memset(vis,false,sizeof(vis));
    memset(dis,inf,sizeof(dis));
    memset(rec,-1,sizeof(rec));
    memset(pre,-1,sizeof(pre));
    queue<int> que;
    que.push(source);
    dis[source]=0;
    vis[source]=true;
    while(!que.empty())
    {
        int node=que.front();
        que.pop();
        vis[node]=false;
        for(int i=head[node];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].capa>0&&dis[v]>dis[node]+edge[i].cost)
            {
                dis[v]=dis[node]+edge[i].cost;
                rec[v]=i;
                pre[v]=node;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                }
            }
        }
    }
    return dis[sink]!=inf;
}
int mcmf()
{
    int maxflow=0;
    int mincost=0;
    while(spfa())
    {
        int node=sink;
        int flow=inf;
        while(node!=source)
        {
            flow=min(flow,edge[rec[node]].capa);
            node=pre[node];
        }
        maxflow+=flow;
        node=sink;
        while(node!=source)
        {
            mincost+=flow*edge[rec[node]].cost;
            edge[rec[node]].capa-=flow;
            edge[rec[node]^1].capa+=flow;
            node=pre[node];
        }
    }
    if(maxflow!=n) return -1;
    return -mincost;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int test;
    scanf("%d",&test);
    for(int cas=1;cas<=test;cas++)
    {
        init();
        scanf("%d%d%d",&n,&K,&G);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&numa[i]);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&numb[i]);
        }
        source=0;
        sink=n*2+1;
        init();
        for(int i=1;i<=n;i++)
        {
            add(source,i,1,0);
            add(i+n,sink,1,0);
            for(int j=1;j<=n;j++)
            {
                if(numb[j]>numa[i])
                {
                    int cost=((numb[j]-numa[i])-K)*((numb[j]-numa[i])-K);
                    add(i,j+n,1,cost>G?G:cost);
                }
            }
        }
        int min_ans=mcmf();
        init();
        for(int i=1;i<=n;i++)
        {
            add(source,i,1,0);
            add(i+n,sink,1,0);
            for(int j=1;j<=n;j++)
            {
                if(numb[j]>numa[i])
                {
                    int cost=((numb[j]-numa[i])-K)*((numb[j]-numa[i])-K);
                    add(i,j+n,1,cost>G?-G:-cost);
                }
            }
        }
        int max_ans=mcmf();
        if(min_ans==-1||max_ans==-1)
        {
            printf("Case %d: impossible\n",cas);
        }
        else
        {
            printf("Case %d: %d %d\n",cas,-min_ans,max_ans);
        }
    }
    return 0;
}

 

posted @ 2019-11-19 21:27  Shmilky  阅读(78)  评论(0编辑  收藏  举报