图论例题合集(三)
目录
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
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;
}