题目链接: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; }