HDU 5943 Kingdom of Obsession 二分图的匹配

题目链接→

s,n 1e9的范围直接暴力匹配二分图肯定会T。。。

对于区间[1,n]和[s+1,s+n],如果存在重叠部分,

                                                                           比如1,2,3......,n-2,   n-1 ,  n

                                                                                                    ↓      ↓      ↓

                                                                                                  s+1,s+2,s+3......s+n 

                                                       s+1,s+2,s+2直接放在s+1,s+2,s+3的位置,只需要去匹配[1,s]和[n,s+n]

这样,我们需要去匹配的区间内就没有s+i可以放在s+i位置的了,如果区间内存在>=2个素数,这些素数只能放在第一个位置,这种情况肯定是No

对于2e9内的数据,打个表可以发现,两个素数间的最大距离为220?还是多少来着,也就是说,如果需要匹配的区间范围超过>440(这里用的450,没啥区别感觉)

的情况肯定也是No,这样我们进行二分图匹配的最大区间范围就缩小到了450,然后就直接用匈牙利算法去进行二分图匹配   get√

边的话……就暴力,可以整除就加边,嗯。

顺便补一补二分图匹配的相关知识,戳这些→二分图的最大匹配、完美匹配和匈牙利算法

       好多博客写的都很棒啊,这里码几篇        二分图匹配(这里有匈牙利和km)

            万能度娘                                           二分图及匹配算法

                                                                      二分图大讲堂

                                                                    二分图常用建图方法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=450; //<--匈牙利-->   二分图匹配 
int t,n,s,cas=1;
vector<int>e[MAX];
map<int,int>match; //存储求解结果 
map<int,int>check;
bool dfs(int u)
{
    for(auto v:e[u])
    {
        if(!check[v])
        {
            check[v]=1;
            if(!match[v]||dfs(match[v]))
            {
                match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
int hungarian()
{
    int ans=0;
    match.clear();
    for(int u=1;u<=n;u++)
    {
        if(!match[u])
        {
            check.clear();
            if(dfs(u))
            ans++;
        }
    }
    return ans;
}
int main()
{
    cin>>t;
    while(t--)
    {
        for(int i=0;i<MAX;i++)
            e[i].clear();
        cin>>n>>s;
        if(s<n)swap(n,s);  //若s<n,[s+1,n]放在原位,二分匹配[n+1,s+n]与[1,s] 
        if(n>MAX){
            cout<<"Case #"<<cas++<<": No"<<endl;
            continue;
        }
    
        for(int i=1;i<=n;i++)
        {
            int temp=i+s;
            for(int j=1;j<=n;j++)
            if(temp%j==0)
             {
              e[j].push_back(temp);
             }
        }
        if(hungarian()==n)
            cout<<"Case #"<<cas++<<": Yes"<<endl;
        else 
            cout<<"Case #"<<cas++<<": No"<<endl;
    }
    return 0;
}

码板子码板子

hungarian

DFS

vector<int>edge[MAX];  //邻接表存储边集
map<int,int>match;     //匹配标记 
map<int,int>vis;       //是否在交替路中
bool dfs(int u)
{
  for(auto v:edge[u])  //在u的邻接点中查询
  {
      if(!vis[v])   //不在交替路中 
    {
          vis[v]=1; //加入到交替路中 
        if(!match[v]||dfs(match[v]))   //为增广路,交换路径,匹配边+1 
        {
          match[v]=u;
          match[u]=v;
          return true; 
        } 
    } 
  }    
  return false; //不存在增广路 
} 
int hungarian()
{
  int ans=0;
  match.claear();
  for(int u=0;u<left_n;u++)
  {
      if(!match[u])  //未匹配
    {
     check.clear();
     if(dfs(u)) ans++; //为增广路,交换路径,匹配边+1 
    } 
  }    
  return ans;
} 

BFS

queue<int>q;
int prev[MAX];  //保存路径 
vector<int>e[MAX];
map<int,int>match;
map<int,int>vis;
int hungarian()
{
    int ans=0;
    match.clear();
    vis.clear();
    for(int u=1;u<=left_n;u++)
    if(!match[i])
    {
     while(!q.empty())
      q.pop();
     q.push(u);
     prev[u]=0; //u为起点
     bool flag=false;//是否找到增广路 
     while(!q.empty()&&!flag) 
     {
         int s=q.front();
        for(auto v:e[s])
        {
            if(vis[v]!=u)
            {
                vis[v]=u;
                q.push(match[v]);
                if(match[v])
                 prev[match[v]]=u;
                else //v为未匹配点,交替路变增广路 
                {
                    flag=true;
                    int d=s,f=v;
                    while(d)
                    {
                        int temp=match[d];
                        match[d]=f;
                        match[f]=d;
                        d=prev[d];
                        f=temp;
                    }
                }
            }      
        }
         q.pop();
      }     
      if(match[u]) ans++; 
    }
    return ans; 
} 

 KM

最佳匹配(带权二分图)

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5;
int w[MAX][MAX];
int match[MAX],usex[MAX],usey[MAX],cx[MAX],cy[MAX];
//match记录右边端点所连的左端点,usex,usey判断是否在增广路上,cx,cy为记录点的顶标
int n,ans,m;
bool dfs(int x)
{
  usex[x]=1;
  for(int i=1;i<=m;i++)
  {
      if(!usey[i]&&cx[x]+cy[i]==w[x][i])
      {                     //如果i未被访问过且为子图的边 
        usey[i]=1;
      if(!match[i]||dfs(match[i]))    
      {                    //若为未匹配点或匹配点可改 
       match[i]=x;
       return true; 
      } 
    }
  }
  return false;    
} 
int km()
{
    for(int i=1;i<=n;i++)
    {
        while(1)
        {
            int d=MAX;
            memset(usex,0,sizeof(usex));
            memset(usey,0,sizeof(usey));
            if(dfs(i)) break; //匹配成功 继续匹配其他点 
            for(int j=1;j<=n;j++)
            {
                if(usex[j])
                 for(int k=1;k<=m;k++)
                     if(!usey[k])
                      d=min(d,cx[j]+cy[k]-w[j][k]);
            }
            if(d==MAX) return -1;
            for(int j=1;j<=n;j++)
                if(usex[j]) cx[j]-=d;
            for(int j=1;j<=m;j++)
                if(usey[j]) cy[j]+=d;  //添加新边 
        }
    }
    ans=0;
    for(int i=0;i<=m;i++)
     ans+=w[match[i]][i];
    return ans; 
 } 
int main()
{
    while(cin>>n>>m)
    {
        memset(cx,0,sizeof(cx));
        memset(cy,0,sizeof(cy));
        memset(w,0,sizeof(w));
        for(int i=1;i<=n;i++)
        {
            int d=0;
            for(int j=1;j<=m;j++) 
            {
              cin>>w[i][j];
              d=max(d,w[i][j]);
            }
            cx[i]=d;
        } 
        memset(match,0,sizeof(match));
        cout<<km()<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Egoist-/p/8227706.html