解题报告:二分出来答案,让小于等于答案的罪犯放在同一个监狱,让大于答案的放在另一个监狱,看是否染色成功。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=20010,M=200010;
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c)
{
   
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int n,m;
int color[N];
bool dfs(int u,int c,int mid)
{
   
    color[u]=c;
    for(int i=h[u];~i;i=ne[i])
    {
   
        int j=e[i];
        if(w[i]<=mid)   continue;
        if(color[j]==3-c)   continue;
        if(color[j]==c) return false;
        if(!color[j])   if(!dfs(j,3-c,mid)) return false;
    }
    return true;
}
bool check(int mid)
{
   
    memset(color,0,sizeof color);
    for(int i=1;i<=n;i++)
    {
   
        if(!color[i])   
       {
   
           if(!dfs(i,1,mid))  
           return false;
           
       }
    }
    return true;
}
int main()
{
   
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--)
{
   
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    add(a,b,c),add(b,a,c);
}
       int l=0,r=1e9;
       while(l<r)
       {
   
           int mid=l+r>>1;
           if(check(mid))   r=mid;
           else l=mid+1;
       }
       printf("%d\n",r);
       return 0;
}

解题报告:由于这题符合二分图染色的奇偶性质(即:不同颜色的格子之间有一条边)问最大的匹配数,枚举每个没有坏的格子,然后匈牙利匹配算法。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=110;
typedef pair<int,int> PII;
bool g[N][N],st[N][N];
PII match[N][N];
int dx[4]={
   1,-1,0,0},dy[4]={
   0,0,1,-1};
   int n,t;
bool find(int a,int b)
{
   
    for(int i=0;i<4;i++)
    {
   
        int tx=a+dx[i];
        int ty=b+dy[i];
        if(tx<=0||ty<=0||tx>n||ty>n||g[tx][ty])  continue;
        if(st[tx][ty])    continue;
        st[tx][ty]=true;
        if(match[tx][ty].first==0||find(match[tx][ty].first,match[tx][ty].second))
        {
   
            match[tx][ty]={
   a,b};
            return true;
        }
    }
    return false;
}
int main()
{
   
    cin>>n>>t;
    while(t--)
    {
   
        int a,b;
        cin>>a>>b;
        g[a][b]=true;
    }
    int res=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(!g[i][j]&&(i+j)%2==1)
    {
   
        memset(st,0,sizeof st);
        if(find(i,j))
        res++;
    }
    cout<<res<<endl;
    return 0;
}

解题报告:这题是最小覆盖模型,由于机器是从0开始的,每变换一次都需要重启,所以我们一开始把含有0的都过滤掉,然后往每个a、b之间连一条边,题目就可以抽象成一个在所有匹配边上选点并且覆盖所有边,结论就等于最大匹配数。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=110;
bool g[N][N];
bool st[N];
int match[N];
int n,m,k;
bool find(int x)
{
   
    for(int i=1;i<m;i++)
    {
   
        if(st[i])   continue;
        if(g[x][i])
        {
    
            st[i]=true;
        int t=match[i];
        if(t==0||find(t))
        {
   
            match[i]=x;
            return true;
        }
        }
    }
    return false;
}
int main()
{
   
    while(cin>>n,n)
    {
   
        cin>>m>>k;
        memset(g,0,sizeof g);
        memset(match,0,sizeof match);
        while(k--)
        {
   
            int id,a,b;
            cin>>id>>a>>b;
            if(a==0||b==0)  continue;
            g[a][b]=true;
        }
        int res=0;
        for(int i=1;i<=n-1;i++)
        {
   
            memset(st,0,sizeof st);
            if(find(i))
            res++;
        }
        cout<<res<<endl;
    }
    return 0;
}

解题报告:这题是最大独立集问题,最大独立集就是集合中的点相互之间没有边相连,最大独立集的数量就是所有点减去最大匹配的数量。在这里还要减去废掉的点。

#include<iostream>
#include<cstring>
using namespace std;
typedef pair<int,int> pii;
const   int N=110;
pii match[N][N];
bool g[N][N];
bool st[N][N];
int n,m,t;
int dx[8]={
   2,-2,1,-1,1,2,-2,-1};
int dy[8]={
   1,1,2,2,-2,-1,-1,-2};
bool find(int x,int y)
{
   
    for(int i=0;i<8;i++)
    {
   
        int tx=x+dx[i];
        int ty=y+dy[i];
        if(tx<=0||ty<=0||tx>n||ty>m)    continue;
        if(!g[tx][ty]||st[tx][ty])  continue;
        st[tx][ty]=1;
        if(match[tx][ty].first==0||find(match[tx][ty].first,match[tx][ty].second))  
        {
   
            match[tx][ty]={
   x,y};
            return true;}
    }
    return false;
}
int main()
{
   
    cin>>n>>m>>t;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    g[i][j]=true;
    for(int i=0;i<t;i++)
    {
   
        int a,b;
        cin>>a>>b;
        g[a][b]=false;
    }
    int res=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
   
        memset(st,0,sizeof st);
        if(g[i][j]&&(i+j)%2&&find(i,j))
        res++;
    }
    cout<<n*m-res-t<<endl;
}

解题报告:这题是最小路径重复点覆盖的问题,先把该问题变成最小路径覆盖问题,即做一遍传递闭包,然后变成所有点减去最大匹配数。其实答案也是出度为0的点个数,就是终点的个数。

#include<iostream>
#include<cstring>
using namespace std;
const   int N=210;
bool g[N][N];
bool d[N][N];
int match[N];
int n,m;
bool st[N];
bool find(int x)
{
   
    for(int i=1;i<=n;i++)
    {
   
        if(d[x][i]&&!st[i])
        {
   
            st[i]=true;
            int t=match[i];
            if(t==0||find(t))
            {
   
                match[i]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
   
    cin>>n>>m;
    while(m--)
    {
   
        int a,b;
        cin>>a>>b;
        g[a][b]=true;
    }
    memcpy(d,g,sizeof g);
    int res=0;
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    d[i][j]|=d[i][k]&d[k][j];
    for(int i=1;i<=n;i++)
    {
   
        memset(st,0,sizeof st);
        if(find(i))
        res++;
    }
    cout<<n-res<<endl;
}