HDU-6808-Go Running(最小点覆盖、网络流)

http://acm.hdu.edu.cn/showproblem.php?pid=6808

题意:张3要求所有人跑步,有一条无限长跑道,每个人可以规定自己跑步的方向,起点,跑步起止时间。另外每个人跑步的速度都是1m/s。张3最后从监控人员哪里得到了n个报告,每个报告给出了某人在某一时候所在的位置,问跑步的最少可能人数是多少。

思路:
以时间和位置为横纵坐标,将这n个报告对应的点画在坐标系上。因为每个人跑步的速度都是1m/s,所以如果两个点所在直线斜率为1或-1,那么他们就有可能是同一个人,换个角度来说就是斜率为±1的直线上的所有点 可能是一个人跑出来的(而且最少一个人)。那么这个问题就转化成用最少的直线(斜率为±1)覆盖所有所有的点。我们将这个图旋转45°,就是二分图中棋盘问题的模型了。

然后用最大流跑二分图最大匹配 O ( m n ) O(m\sqrt{n} ) O(mn ):通过点建立“横坐标”和“纵坐标”之间的连接,然后建立源点到“横坐标”、“纵坐标”到汇点的连接。

//加上源点和汇点,点数上限为2e5+2
//边数上限为3e5*3
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int manx=2e5+10;
const int manx2=6e5+10;

int cou,s,t;
int deep[manx],X[manx],Y[manx],x[manx],y[manx];
int head[manx],cur[manx];
int flow[manx];
struct node
{
    int e,bf,w;
}edge[manx2];
inline void add(int s,int e,int w)
{
    edge[++cou]=node{e,head[s],w};
    head[s]=cou;
}

int bfs(int n)
{
    for(int i=0;i<=n;i++)cur[i]=head[i],deep[i]=-1;
    queue<int>qu;
    qu.push(s);
    deep[s]=0;
    while(!qu.empty())
    {
        int now=qu.front();
        qu.pop();
        for(int i=head[now];~i;i=edge[i].bf)
        {
            int net=edge[i].e;
            if(edge[i].w>0&&deep[net]==-1)
            {
                deep[net]=deep[now]+1;
                qu.push(net);
            }
        }
    }
    return deep[t]!=-1;
}
//因为把flow上限写成2,T到飞起
int dfs(int now=s,int flow=1e8)
{
    if(now==t)
        return flow;
    int ans=0;
    for(int i=cur[now];~i&&flow;i=edge[i].bf)
    {
        cur[now]=i;
        int net=edge[i].e;
        if(edge[i].w>0&&deep[net]==deep[now]+1)
        {
            int temp=dfs(net,min(flow,edge[i].w));
            flow-=temp,ans+=temp;
            edge[i].w-=temp;
            edge[i^1].w+=temp;
        }
    }
    return ans;
}
int Dinic(int n)
{
    int ans=0;
    while(bfs(n))
        ans+=dfs();
    return ans;
}
int main()
{
    int T,n,x2,t2;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        pair<int,int>point[manx];
        for(int i=0;i<n;++i)
        {
            scanf("%d%d",&t2,&x2);
            int l=x2+t2,r=x2-t2;
            point[i].first=l,point[i].second=r;
        }
        sort(point,point+n);
        n=unique(point,point+n)-point;//去重点
        for(int i=0;i<n;i++)
        {
            X[i]=x[i]=point[i].first;
            Y[i]=y[i]=point[i].second;
        }
        //sort(X,X+n);
        sort(Y,Y+n);
        int cou1=unique(X,X+n)-X;
        int cou2=unique(Y,Y+n)-Y;
        s=cou1+cou2,t=s+1;
        cou=-1;
        for(int i=0;i<=t;++i)head[i]=-1;
        for(int i=0;i<cou1;++i)//建立源点到“横坐标”、“纵坐标”到汇点的连接
            add(s,i,1),add(i,s,0);
        for(int i=0;i<cou2;++i)
            add(i+cou1,t,1),add(t,i+cou1,0);
        for(int i=0;i<n;++i)
        {//建立“横坐标”和“纵坐标”之间的连接
            int x1=lower_bound(X,X+cou1,x[i])-X;
            int y1=lower_bound(Y,Y+cou2,y[i])-Y+cou1;
            add(x1,y1,1);
            add(y1,x1,0);
        }
        printf("%d\n",Dinic(t));
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=6806(dp)

设dp[i]表示前i个单词的"almost-equal"的个数
str[i]==str[i-1]时,dp[i]=dp[i-1];
str[i]!=str[i-1]时,dp[i]=dp[i-1]+dp[i-2]

//有的人不仅写串变量名,
HDU-6808-Go Running(最小点覆盖、网络流)_第1张图片
//加法取模也不加括号
HDU-6808-Go Running(最小点覆盖、网络流)_第2张图片

#include
#include
#include
//#define int long long
using namespace std;
const int mod=1e9+7;
const int manx=1e5+7;

string a[manx];
signed main()
{
    int t,n,dp[manx];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            cin>>a[i];
        dp[1]=dp[2]=1;
        if(a[1]!=a[2])dp[2]=2;
        for(int i=3;i<=n;i++)
            if(a[i]!=a[i-1])
                dp[i]=(dp[i-1]+dp[i-2])%mod;
            else dp[i]=dp[i-1];
        printf("%d\n",dp[n]);
    }
    return 0;
}

你可能感兴趣的:(网络流,背包dp)