2015 多校赛 第五场 1010 (hdu 5352)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5352

看题看得心好累。

 

题目大意:

给出 n 个点,依次执行 m 次操作:输入“1 x”时,表示将与 x 连通的点全部修复;输入“2 x y”,表示在 x 与 y 之间加一条边;输入“3 x y”,表示删除 x 与 y 之间的边。题目确保不会与重边并且操作合法。

题目会给出 k,要求每次修复的的点的数目小于等于k。

问:怎样执行操作1,使得令修复点数最多的同时,令每次执行操作1所修复的点的数目所构成的数列字典序最小。(可以令某次操作无效,或者说令其修复的点数为0)

 

解题思路:

二分图最大匹配、拆点。

(我反正是看题解才弄明白的。。)

 

具体实现过程:

首先我们知道二分图最大匹配的用途。在这道题中,左侧点对应于每次操作1,右侧点即所有的点。然后边对应于——每次操作1修复的点与之相连通的点 之间的边。则寻找最大匹配即为寻找在所有操作下所能得到的最大匹配数。

显然操作2与操作3都是为操作1而服务的,直接相关于与操作1所操作的点所连通的点(好累赘。。)。每次执行操作1时,记录与该点相连通的所有点,并给该点与所连通的点建边。这里注意:拆点。将每个点都拆成 k 个点。这样的话,每个操作所得的最大匹配数<=k,即每个操作所修复的点的个数<=k。

图已建好,跑一遍匈牙利算法就可以了(不考虑字典序的情况下)。

要考虑字典序的话,由匈牙利算法的特性易知,则从最后一次操作往前跑匈牙利算法即可。

容易思维卡壳的是,对于每次操作1,与点 x 相连通的点<=k时,全部都选了不就可以了吗等等。

需要注意的是,当下操作得到最大值不一定使得最后的最优值。即局部最优解未必能得到全局最优解,所以才需要用二分图最大匹配算法。

 

注意对maxn的大小设定。

然后是,这个做法最后做出来是700ms左右,不太理想。据说还可以用最大流和最小费用最大流做。

考虑不换算法的优化的话,首先是用数组模拟邻接表,因为用vector的push_back和clear操作耗时颇大。尤其在初始化调用g[i].clear()的时候,i的边界的设定如果过大的话会导致TLE。具体会快多少不好说。

然后是匈牙利算法换成bfs实现。晚点再写一下看能快多少。

 

这是上次华师校赛后第二次碰到二分图最大匹配的题,充分体现的该算法理解的不足。模板题和应用题毕竟两回事。

 

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 555
int n,m,k,T,op,a,b,c,tot,ans,ary[maxn],pic[maxn][maxn];
vector<int>g[maxn*maxn];
int con[maxn],vis[maxn],con_tot[maxn],divp;
void init(){
    memset(pic,0,sizeof(pic));
    memset(ary,0,sizeof(ary));
    memset(con,0,sizeof(con));
    divp=ans=tot=0;
    for(int i=0;i<=n*k;i++) g[i].clear();
}
void find_con(int u){
    vis[u]=1;
    con_tot[tot++]=u;
    for(int i=1;i<=n;i++) if(!vis[i]&&pic[u][i])
        find_con(i);
}
int dfs(int u){
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!vis[v]){
            vis[v]=1;
            if(!con[v]||dfs(con[v])){
                con[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
void solve(){
    for(int i=divp-1;i>=0;i--){
        for(int j=i*k,lim=(i+1)*k;j<lim;j++){
            memset(vis,0,sizeof(vis));
            if(dfs(j)){
                ans++;
                ary[i]++;
            }
        }
    }
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        init();
        for(int i=0;i<m;i++){
            scanf("%d",&op);
            if(op==1){
                scanf("%d",&a);
                tot=0;
                memset(vis,0,sizeof(vis));
                find_con(a);
                for(int j=0;j<tot;j++){
                    for(int t=k*divp,lim=k*(divp+1);t<lim;t++)
                        g[t].push_back(con_tot[j]);
                }
                divp++;
            }
            else if(op==2){
                scanf("%d%d",&a,&b);
                pic[a][b]=pic[b][a]=1;
            }
            else{
                scanf("%d",&c);
                for(int j=0;j<c;j++){
                    scanf("%d%d",&a,&b);
                    pic[a][b]=pic[b][a]=0;
                }
            }
        }
        solve();
        printf("%d\n",ans);
        for(int i=0;i<divp;i++)
            printf("%d%c",ary[i],i==divp-1?'\n':' ');
    }
    return 0;
}
View Code

 

posted @ 2015-08-08 17:16  轶辰  阅读(166)  评论(0编辑  收藏  举报