2019 Niuke Summer Multi-School Training Camp (Part 2) F Partition problem [DFS brute force search + optimization]

Title description

Given 2N people, you need to assign each of them into either red team or white team such that each team consists of exactly N people and the total competitive value is maximized.

 

Total competitive value is the summation of competitive value of each pair of people in different team.

 

The equivalent equation is \sum_{i=1}^{2N}\sum_{j=i+1}^{2N}(v_i_j if i-th person is not in the same team as j-th person else 0)

Enter description

The first line of input contains an integers N.

Following 2N lines each contains 2N space-separated integers v_i_j is the j-th value of the i-th line which indicates the competitive value of person i and person j.

* 1≤N≤14
* 0≤v_i_j≤109
* v_i_j=v_j_i

Output description

Output one line containing an integer representing the maximum possible total competitive value.

 Example input

1
0 3
3 0

Sample output

3

The main idea:

There are 2N individuals, to be divided into two teams, each team N people. There is a v value between everyone in one team and everyone in the other team, and the maximum sum of v values ​​that can be obtained after being divided into two teams is required (the specific summation method is given by the formula in the question) .

analysis:

Looking at the data of the topic, the maximum N is 14, which means that the worst case is C_2_8^1^4that it can reach violent search when it reaches about 4e7. The general idea is to use DFS to select N individuals from 2N individuals to form a team, and the rest to form another team. But if this is to calculate all the v until the end of each case and the search because every time n^2complexity, time out. So here we use each case to dynamically maintain the sum of v in the search process, so that each case has only n complexity.

See the code for specific explanation.

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>

#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long

using namespace std;

ll val[30][30];
ll maxx=0;
int vis[30];//0表示在本队,1表示在另一队 
int n;

void dfs(int index,int cnt,ll tmpsum){
	if(index>2*n)  return;//下标超过2N,返回 
	if(2*n-index+cnt<n)  return;//剩下的人数已经不够选出N个人,返回 
	if(cnt>=n){
		//在这里计算和值会超时 
//		ll tmp=0;
//		rep(i,1,2*n){
//			rep(j,i+1,2*n){
//				if(vis[i]!=vis[j])  tmp+=val[i][j];
//			}
//		}
		maxx=max(maxx,tmpsum);
//		cout<<"t"<<tmpsum<<endl;
		return;
	}
	ll nxtsum=tmpsum;
	//选择当前下标的人去"1"队 
	vis[index]=1;
	rep(i,1,2*n){
		if(vis[i]!=1){
			nxtsum+=val[i][index];//当前下标的人和"0"队所有的人的v值加入和值 
		}
		else{
			nxtsum-=val[i][index];//扣除当前下标的人和"1"队所有人的v值(在之前过多的加入了) 
		}
	}
	dfs(index+1,cnt+1,nxtsum);
	//选择当前下表的人留在"0"队 
	vis[index]=0;
	dfs(index+1,cnt,tmpsum);
}

int main(){
	scanf("%d",&n);
	rep(i,1,2*n){
		rep(j,1,2*n){
			scanf("%lld",&val[i][j]);
		}
	}
	memset(vis,0,sizeof(vis));
	dfs(1,0,0);
	cout<<maxx<<endl;
	return 0;
}

 

Published 30 original articles · won 5 · 900 views

Guess you like

Origin blog.csdn.net/qq_42840665/article/details/97132978