当前位置: 代码迷 >> 综合 >> 1011 - Marriage Ceremonies (状压DP(dfs))
  详细解决方案

1011 - Marriage Ceremonies (状压DP(dfs))

热度:55   发布时间:2023-12-12 14:21:22.0

题目连接

Time Limit: 2 second(s) Memory Limit: 32 MB
You work in a company which organizes marriages. Marriages are not that easy to be made, so, the job is quite hard for you.

The job gets more difficult when people come here and give their bio-data with their preference about opposite gender. Some give priorities to family background, some give priorities to education, etc.

Now your company is in a danger and you want to save your company from this financial crisis by arranging as much marriages as possible. So, you collect N bio-data of men and N bio-data of women. After analyzing quite a lot you calculated the priority index of each pair of men and women.

Finally you want to arrange N marriage ceremonies, such that the total priority index is maximized. Remember that each man should be paired with a woman and only monogamous families should be formed.

Input
Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case contains an integer N (1 ≤ n ≤ 16), denoting the number of men or women. Each of the next N lines will contain N integers each. The jth integer in the ith line denotes the priority index between the ith man and jth woman. All the integers will be positive and not greater than 10000.

Output
For each case, print the case number and the maximum possible priority index after all the marriages have been arranged.

Sample Input
2
2
1 5
2 1
3
1 2 3
6 5 4
8 1 2

Output for Sample Input
Case 1: 7
Case 2: 16

题目的大意就不多少了,很简单,我之前想过了很多方法,有普通的dfs遍历每一种情况,该方法可定会是超时的,毕竟是 16!的复杂度嘛,但还是想练一练,测了一个16数据的样例,跑了还就都没有出结果,想着优化一下,但是没有想出来,第二种找出排序规则用结构体数组排序的出最大值,找了两个排序的规则,不过都是错误的都是有bug的。后来就放弃了,去网上搜了搜题解,了解了一种新的方法:状态压缩DP,简称状压DP。这个我之前好像写过类似的题,但不是用二进制储存状态的,那个提的数据量也许会比较少。说说这个状压Dp:对于少许状态的题目(尽量不能超过16个状态, 因为太大了,开的dp数组也会很大有可能会超出题目内存限制),可以用二进制的每一位上面的0 或者1 两种状态表示,具有标记的作用,其中会用到二进制的位运算位运算,不太熟悉位运算的朋友可以网上搜一下看看,出了这个二进制储存状态的特点之外,还有一个特点就是复用(数据的重复利用),大家可以对比一下下面的连个代码,本质上的方法都是遍历所有状态,找出最大值,但是dp的那个里面的dp数组具有储存功能,储存了之前递归过程中已经得到的数据,然后后面的帝国就可以利用这些数据,从而大大的减少递归的次数,我测试了一下最大数据的时候 递归的次数为 524288,这个数字相比较 16! 小得太多太多了, 所以时间没有超时,成功的AC了这道题目!!!

普通超时的dfs代码

#include <iostream> 
#include <cstdio> 
#include <algorithm> 
#include <cstring> 
using namespace std;  
const int maxn = 20;  
int dp[maxn][maxn], book[maxn], max1, n;  
void dfs(int i, int ans)  
{  if(i == n+1)  {  if(ans > max1) max1 = ans;  return;  }  for(int j = 1; j <= n; j++)  {  if(book[j])  continue;  book[j] = 1;  ans += dp[i][j];  dfs(i+1, ans);  book[j] = 0;  ans -= dp[i][j];  }  
}  
int main()  
{  int t, ca = 1;  scanf("%d", &t);  while(t--)  {  memset(book, 0, sizeof(book));  scanf("%d", &n);  for(int i = 1; i <= n; i++)  for(int j = 1; j <= n; j++)  scanf("%d", &dp[i][j]);  max1 = -1;  dfs(1,0);  printf("Case %d: %d\n",ca++, max1);  }  return 0;  
}  

**状态压缩DP代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;int dp[(1 << 16) + 10];// dp[i] 表示 i 的时候的最大值
int marry[20][20];// 输入存储数据
int n;int dfs(int mark, int row)// mark 做来做标记(16位二进制进行0或者1 的标记)
{if(mark == (1 << n) - 1)// 递归结束的条件(mark已经标记了 16 个 1了)return dp[mark] = 0;if(dp[mark] != -1) // 利用之前递归过程中得到了数据,减少递归次数return dp[mark];for(int j = 0; j < n; j++)// 进行循环递归遍历每一种情况,比较结果的出最大值{if(!(mark & (1 << j)))dp[mark] = max(dp[mark], dfs(mark | (1<<j), row + 1) + marry[row][j]);}return dp[mark];
}int main()
{int t, countt = 0;scanf("%d", &t);while(t--){scanf("%d", &n);memset(dp, -1, sizeof(dp));for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)scanf("%d", &marry[i][j]);printf("Case %d: %d\n", ++countt, dfs(0, 0));}return 0;
}
  相关解决方案