2016-2017 CT S03E01: Codeforces Trainings Season 3 Episode 1 J Wrong Answer 最大独立集

题意:

一个最大为2000*2000的矩阵,有h个横着的单词,v个竖着单词,横着的可能与竖着的有交叉,交叉的字符只能选其一放在格子内,问怎样选择才能使矩阵内单词的数量最多,输出最多有多少个。(注意同一行的单词不会有交叉)

解法:

最大独立集,因为横着的与横着不会有交叉,竖着的没有,所以可以把横着的单词放在一边,竖着的放在另一边,横着的与竖着的交叉点是不同字母的进行连边。跑一遍二分匹配,每一个匹配就可以理解成这条边匹配的两个点只能有一个成立。最后的答案就是总共的点数减去匹配数就行了。

#include 
using namespace std;
const int N = 2000+10;

struct node{
    int v,next;
}E[N*100];
int h,v,top;
char s[N][N];  ///存单词的图
int head[N];
int num[N][N]; ///存单词的编号
bool vis[N];
int match[N];  ///存二分匹配的点

void Init()
{
    top = 0;
    for(int i = 0;i < N;i++){
        head[i] = match[i] = -1;
        for(int j = 0;j < N;j++){
            s[i][j] = '\0';
            num[i][j] = 0;
        }
    }
}

void add(int u,int v)
{
    E[top].v = v;
    E[top].next = head[u];
    head[u] = top++;
}

bool dfs(int u)
{
    for(int i = head[u];i != -1;i = E[i].next){
        int v = E[i].v;
        if(vis[v]) continue;
        vis[v] = true;
        if(match[v] == -1 || dfs(match[v])){
            match[v] = u;
            return true;
        }
    }
    return false;
}

void xyl()
{
    int ans = 0;
    for(int i = 1;i <= h;i++){
        memset(vis,false,sizeof vis);
        if(dfs(i)){
            ans++;
        }
    }
    ans = h+v-ans;
    printf("%d\n",ans);
}

int main(void)
{
    int T;
    scanf("%d",&T);
    char str[1010];
    while(T--){
        Init();
        scanf("%d%d",&h,&v);   ///总共有h+v个点
        for(int i = 1;i <= h;i++){
            int x,y;
            scanf("%d%d%s",&x,&y,str);
            swap(x,y);
            int len = strlen(str);
            for(int j = 0;j < len;j++){
                if(s[x][y+j] != str[j] && num[x][y+j] != 0){
                    add(num[x][y+j],i);
                    add(i,num[x][y+j]);
                }
                s[x][y+j] = str[j];
                num[x][y+j] = i;
            }
        }
        for(int i = 1;i <= v;i++){
            int x,y;
            scanf("%d%d%s",&x,&y,str);
            swap(x,y);
            int len = strlen(str);
            for(int j = 0;j < len;j++){
                if(s[x+j][y] != str[j] && num[x+j][y] != 0){ ///只有横着与竖着交叉点字符不一样才建边
                    add(num[x+j][y],h+i);
                    add(h+i,num[x+j][y]);
                }
                s[x+j][y] = str[i];
                num[x+j][y] = h+i;
            }
        }
        xyl();  ///匈牙利
    }
    return 0;
}


你可能感兴趣的:(图论)