hdu(3225) 求第k小匹配(09上海站F题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3225


题意描述:给你M*N的格子,N种花,每个格子种一种花,每一行种N中不同的花且每一列不能有相同的花出现,现在要求增加一行后也满足这个条件,让你求出按字典序第k大的一种种法,如果小于k种,那么直接输出-1


分析:该行的每一个位置都可以种一些满足条件的花,可以这样建图,每一个位置可以和多种花连边,求第k完备匹配即是答案,那么怎么求第k小完备匹配呢??

这里我们就可以用搜索来解,按字典序进行搜索,枚举出一种情况就判断该情况是否是一个完备匹配,这样求出来的第k个即为答案,显然这题不会这么水,毕竟是个匹配有关的题目,所以一定会用到匹配的,这里我们就可以用匹配算法来进行剪枝,我们先求出一个完备匹配,然后搜索每个位置能够种的花,假设当前位k置种了花i,那么判断k+1--n位置能不能形成一个完备匹配(即能否种出满足条件的花),若能那么当前位置可以种该花,继续搜索,若不能这返回


详情见代码:


#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int N=300;
bool vis[N][N];
int g[N][N];
int link[N];//link里面存放的是花对应放的位置的编号
bool used[N];
bool visit[N];
int n,link1[N],k,num,res[N];
bool find(int t,int limit)//找出比点得编号比limit大的匹配
{
    if(t<=limit)return false;
    int i;
    for(i=1;i<=n;i++)
    if(g[t][i]&&!used[i])
    {
        used[i]=true;
        if(link[i]==-1||find(link[i],limit))
        {
            link[i]=t;
            return true;
        }
    }
    return false;
}

bool check(int pid,int fid) //判断假设pid和fid匹配之后,看pid+1--n个位置是否能够形成完备匹配
{
    if(link[fid]==pid)return true;
    int i,j;
    for(i=1;i<=n;i++)
    {
        link1[i]=link[i];
        if(link[i]==pid)j=i;
    }
    int t=link[fid]; link[fid]=pid; link[j]=-1; //将花fid和位置pid匹配,则与花原来匹配的位置则需从新匹配
    memset(used,0,sizeof(used));
    if(find(t,pid)) return true;
    else for(i=1;i<=n;i++)
    link[i]=link1[i];
    return false;

}
bool dfs(int depth)
{
    if(depth==n+1)
    {
        num++;
        if(num==k)
        return true;
        return false;
    }
    for(int i=1;i<=n;i++)
    if(!visit[i]&&g[depth][i]&&check(depth,i))
    {
        visit[i]=true;
        res[depth]=i;
        if(dfs(depth+1))return true;
        visit[i]=false;

    }
    return false;
}
int main ()
{
    int t,cas=1,m,i,j,x;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&k);
        memset(vis,0,sizeof(vis));
        //vis[i][j]为1的时候表示第i行第j个位置种了第j种花
        
        for(i=1;i<=m;i++)
        for(j=1;j<=n;j++)
        {
            scanf("%d",&x);
            vis[j][x]=true;
        }

        memset(g,0,sizeof(g));
        for(j=1;j<=n;j++)
        for(i=1;i<=n;i++)
        if(!vis[i][j])
        g[i][j]=1;//表示第j个位置可以放第i中花
        printf("Case #%d:",cas++);
        memset(link,-1,sizeof(link));
        for(i=1;i<=n;i++)
        {
            memset(used,0,sizeof(used));
            if(!find(i,0))
            break;
        }
        if(i<=n){printf(" -1\n");continue;}
        num=0;
        memset(visit,0,sizeof(visit));
        bool tag=dfs(1);
        if(tag)
        for(i=1;i<=n;i++)
        printf(" %d",res[i]);
        else printf(" -1");
        printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值