算法竞赛入门经典 写题笔记(第五章 图论算法与模型4)

本节内容——

  • 网络流问题及常见模型

例题27 我是SAM

说是SAM但是并没有什么关系
给出一个R*C大小的网络,网格上面放了一些目标。可以在网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标。你的任务是计算出最少需要多少子弹,各从哪些位置发射,才能把所有目标全部打掉。(还要输出任意一种合法方案)

就是一个二分图最小覆盖问题,把X坐标(一行)和Y坐标(一列)划分成二分图,如果有一个目标位置为(x,y),那么连行X和列Y。
然后因为二分图最小点覆盖即最小割,我们直接上dinic即可。
麻烦的是如何输出任意一组最小割方案——
(如果只输出左右端点,不考虑中间连边——即INF时)跑一遍dinic,然后在残量网络上记录done数组,最后一遍bfs左边没有访问到的点和右边访问到的点就是最小割。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define S 0
#define T n+m+1
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,t,k,top,tot,cnt;
int head[MAXN],cur[MAXN],dis[MAXN],done[MAXN];
int dfn[MAXN],low[MAXN],c[MAXN],st[MAXN],in[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
struct Node{char op;int pos;};
vector<Node>vec;
inline void add(int from,int to,int dis)
{
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool bfs()
{
    queue<int>q;
    memset(dis,0x3f,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    memset(done,0,sizeof(done));
    q.push(S);dis[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].dis&&dis[v]==0x3f3f3f3f)
                dis[v]=dis[u]+1,q.push(v),done[v]=1;
        }
    }
    if(dis[T]==0x3f3f3f3f) return false;
    return true;
}
inline int dfs(int x,int f)
{
    if(!f||x==T) return f;
    int used=0,w;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        cur[x]=i;
        int v=edge[i].to;
        if(dis[v]==dis[x]+1&&(w=dfs(v,min(f,edge[i].dis))))
        {
            edge[i].dis-=w,edge[i^1].dis+=w;
            f-=w,used+=w;
            if(!f) break;
        }
    }
    return used;
}
inline int dinic()
{
    int cur_ans=0;
    while(bfs()) cur_ans+=dfs(S,(int)1e9);
    return cur_ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    while(scanf("%d%d%d",&n,&m,&k)==3)
    {
        if(n==0&&m==0&&k==0) break;
        vec.clear();
        memset(head,0,sizeof(head));
        t=1,top=tot=cnt=0;
        for(int i=1;i<=k;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y+n,INF);//printf("[%d %d] %d\n",x,y+n,INF);
        }
        for(int i=1;i<=n;i++) add(S,i,1);//printf("[%d %d] %d\n",S,i,1);
        for(int i=1;i<=m;i++) add(i+n,T,1);//printf("[%d %d] %d\n",i+n,T,1);
        int ans=dinic();
        printf("%d ",ans);
        for(int i=1;i<=n;i++)
            if(!done[i])
            {
                Node cur;
                cur.op='r',cur.pos=i;
                vec.push_back(cur);
            }
                // printf("r%d ",i);
        for(int i=1;i<=m;i++)
            if(done[i+n])
            {
                Node cur;
                cur.op='c',cur.pos=i;
                vec.push_back(cur);
            }
                // printf("c%d ",i);
        for(int i=0;i<vec.size()-1;i++) printf("%c%d ",vec[i].op,vec[i].pos);
        printf("%c%d\n",vec[vec.size()-1].op,vec[vec.size()-1].pos);
    }
    return 0;
}

例题28 保守的老师

翻译自己看去.......
就是4个条件只要满足任意一个就要选取,也就是——4个条件全部不满足只能任选其一。那么我们考虑只要4个条件都不满足,就连一条边,然后跑二分图最大独立集就行了。(可以根据是男生还是女生分成二分图)
二分图最大独立集等于n-最大流。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define S 0
#define T n+1
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
int tt,n,m,t,tot;
int head[MAXN],dis[MAXN],cur[MAXN];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
struct Node{int a,id;char b;string c,d;}node[MAXN];
inline void add(int from,int to,int dis)
{
    // if(from!=S&&to!=T) printf("[%d %d]\n",from,to);
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0,head[to]=t;
}
inline bool bfs()
{
    memset(dis,0x3f,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    queue<int>q;
    q.push(S);
    dis[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dis[v]==0x3f3f3f3f&&edge[i].dis)
            {
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    if(dis[T]==0x3f3f3f3f) return false;
    return true;
}
inline int dfs(int x,int f)
{
    if(x==T||!f) return f;
    int used=0,w;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        cur[x]=i;
        if(dis[edge[i].to]==dis[x]+1&&(w=dfs(edge[i].to,min(f,edge[i].dis))))
        {
            used+=w,f-=w;
            edge[i].dis-=w,edge[i^1].dis+=w;
            if(!f) break;
        }
    }
    return used;
}
inline int dinic()
{
    int cur_ans=0;
    while(bfs()) cur_ans+=dfs(S,INF);
    return cur_ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    scanf("%d",&tt);
    while(tt--)
    {
        memset(head,0,sizeof(head));
        tot=0;
        t=1;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            cin>>node[i].a>>node[i].b>>node[i].c>>node[i].d;
            if(node[i].b=='M') tot++;
        }
        int num1=1,num2=1;
        for(int i=1;i<=n;i++)
        {
            if(node[i].b=='M') 
                node[i].id=num1,num1++,add(S,node[i].id,1);
            else 
                node[i].id=num2+tot,num2++,add(node[i].id,T,1);
        }
        // for(int i=1;i<=n;i++)
        //     printf("id=%d\n",node[i].id);
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(i==j) continue;
                int flag=0;
                if(abs(node[i].a-node[j].a)<=40) flag++;//cout<<1<<endl;
                if(node[i].b!=node[j].b) flag++;//cout<<2<<endl;
                if(node[i].c==node[j].c) flag++;//cout<<3<<endl;
                if(node[i].d!=node[j].d) flag++;//cout<<4<<endl;
                if(flag!=4) continue;
                if(node[i].b=='M') add(node[i].id,node[j].id,1);
                else add(node[j].id,node[i].id,1);
            }
        }
        printf("%d\n",n-dinic());
    }
    return 0;
}

例题29 出租车

有m个人从城市的不同位置出发,到达它们各自的目的地。已知每个人的出发时间,出发地点和目的地。你的任务是用尽量少的出租车送他们,使得每次出租车接待客人时,至少能提前一分钟到达他所在的位置。注意,为了满足这一条件,要么这个人是这辆出租车接送的第一个人,要么在接送完上一个人后,有足够的时间从上一个目的地开到这里。

猜你喜欢

转载自www.cnblogs.com/fengxunling/p/10851313.html