hihocoder week170分析---最大子完全图

题目

描述:

Given N words from the top 100 common words in English (see below for reference), select as many words as possible as long as no two words share common letters.

Assume that the top 100 common words in English are:

the be to of and a in that have i it for not on with he as you do at this but his by from they we say her she or an will my one all would there their what so up out if about who get which go me when make can like time no just him know take people into year your good some could them see other than then now look only come its over think also back after use two how our work first well even new want because any these give day most us


输入:

The first line contains an integer N, denoting the number of words. (1 ≤ N ≤ 40)  

The second line contains N words from the top 100 common words.

输出:

Output the most number of words can be selected.


样例输入:

8
the be to of and a in that

样例输出:

4

分析:

我们首先会想到,将所有没有公共字母的字符串连一条边,之后我们得到了一个图,在这个图上,我们求取最大完全子图,也就是最大团。


最大团的算法,我们使用dfs+dp(剪枝)的方法进行求解。

使用的数据结构:

(1) 堆栈stack[I][j]:进行第I次dfs时候,我们将进行第I+1次dfs需要的所有点放到堆栈中。

(2) dp[I]:从节点I到n-1之间的最大团节点数

dfs的步骤(进行第step步dfs):

1. 如果当前的stack[step]没有元素,返回

2. 遍历所有stack[step]中的所有节点k=stack[step][j],

    (1)如果(之前计算的最大团节点数(step) + k之后的所有节点(n-k))< 当前最大团节点数(mx)),进行剪枝

    (2)如果(之前计算的最大团节点数(step) + k之后的所有节点组成的最大团节点数(dp[k]))< 当前最大团节点数(mx)),进行剪枝


代码如下:

#include <iostream>
#include <cstring>
#include <string>
#define SIZE 102

using namespace std;

int mat[SIZE][SIZE];  /*图矩阵*/
int dp[SIZE];
int mx;
int stack[SIZE][SIZE];
void dfs(int N,int num,int step){
	// stack中没有剩余的节点,返回
    if(num==0){
        if(step > mx){
            mx=step;
        }
        return ;
    }

    for(int i=0;i<num;i++){
        int k = stack[step][i];
        // 剪枝1
        if(step+N-k<=mx) return ;
        // 剪枝2
        if(step+dp[k]<=mx) return ;
        int cnt = 0;
        for(int j=i+1;j<num;j++)
            if(mat[k][stack[step][j]]){
                 stack[step+1][cnt++]=stack[step][j];
            }
        dfs(N,cnt,step+1);
    }
}

void run(int N){
    mx =0;
    for(int i=N-1;i>=0;i--){
        int sz =0;
        for(int j=i+1;j<N;j++)
            if(mat[i][j]) stack[1][sz++]=j;
        dfs(N,sz,1);
        dp[i]=mx;
    }

    cout << dp[0] << endl;
}

bool hasSame(string &s1, string &s2){
	bool has[30];
	memset(has, 0, sizeof(has));

	// cout << s1 << " " << s2 << endl;
	for(int i = 0; i < s1.size(); i++){
		has[s1[i] - 'a'] = true;
	}

	for(int i = 0; i < s2.size(); i++){
		if(has[s2[i] - 'a']) return false;
	}

	return true;
}


int n;
string all[50];

int main(int argc, char const *argv[])
{
#ifndef ONLINE_JUDGE
	freopen("in", "r", stdin);
#endif
	cin >> n;
	memset(mat, 0, sizeof(mat));

	for(int i = 0; i < n; i++){
		cin >> all[i];
	}

	mx =0;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			if(i == j) continue;
			if(hasSame(all[i], all[j])) mat[i][j] = 1;
		}
	}

	run(n);


	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值