分书问题(搜索+回溯+剪枝)

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
已知有n本书(从1~n编号)和n个人(从1~n编号),每个人都有一个自己喜爱的书的列表,现在请你编写一个程序,设计一种分书方案,使得每个人都能获得一本书,且这本书一定要在他的喜爱列表中。
输入
输入数据共若干行,第一行为一个正整数n(n <= 20),从第2行到第n+1行,每行有n个0或1组成,第k行表示编号为k-1的人对这n本书的喜好列表,0表示不喜欢,1表示喜欢。
输出
输出数据仅一个整数,表示符合条件的分配方案的总数。
样例输入 Copy

5
00110
11001
01100
00010
01001

样例输出 Copy

1

典型的工作分配问题。工作分配问题就是寻找这样的一个组合,使得一项工作仅由一位工人完成,一位工人只完成一项工作。
这个题就是让每个人拿到各不相同的书,求每个人拿到的书都是他喜欢的方案数。
采用搜索+回溯的方法。要明确是书分配到人,因此可以从书入手。首先用book[i][j]表示第i个人对第j本书的喜爱与否,v[i]表示第i本书是否被选中。利用for循环从第一个人分书,直到所有人都分到书。在检查第j本书时,如果该书没有被分配到,则标记为被分配到,然后对下一个人分书,直到对所有人分完书后回溯到上一个人,一直进行下去,直到回溯到第一个人后就能得到所有的可行解。我们只需要得到所有符合条件的分配方案,因此在搜索的时候用sum变量记录喜欢值之和,最后符合条件的一定是和等于n对应的方案。另外,在搜索的时候可能会有不符合要求的方案,根据题意可知在搜索过程中如果当前的喜欢值之和+1小于当前的深度,则说明一定有人分配到喜欢值为0的书,最后得到的一定不是最优解,此时应该利用剪枝将其舍去。(此题如果不进行剪枝会TLE)

#include<cstdio>
using namespace std;
char book[25][25];
int v[25]={
    
    0};
int n,cnt=0;
void f(int deep,int sum)
{
    
    
    if(deep==n+1)//到达n+1层时说明已对n个人分配了书
    {
    
    
        if(sum==n)cnt++;
        return;
    }
    if(deep<=n&&sum+1<deep)return;//剪枝,找一下规律即可发现不符合要求时满足的条件就是sum+1<deep
    for(int j=1;j<=n;j++)
    {
    
    
        if(v[j]==0)//没有被分配
        {
    
    
            v[j]=1;
            f(deep+1,sum+book[deep][j]-'0');//搜索+回溯
            v[j]=0;
        }
    }
}
int main()
{
    
    
    int i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
    
    
        scanf("%s",book[i]+1);
    }
    f(1,0);
    printf("%d",cnt);
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:130 ms
    Memory:1120 kb
****************************************************************/

猜你喜欢

转载自blog.csdn.net/upc122/article/details/105759803