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;
}

时间: 2024-10-12 21:04:48

2015 多校赛 第五场 1010 (hdu 5352)的相关文章

2015 多校赛 第四场 1010 (hdu 5336)

Problem Description XYZ is playing an interesting game called "drops". It is played on a r∗c grid. Each grid cell is either empty, or occupied by a waterdrop. Each waterdrop has a property "size". The waterdrop cracks when its size is

2015 多校赛 第五场 1009 (hdu 5348)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5348 题目大意:给出一幅无向图,问是否存在一种方案,使得给每条边赋予方向后,每个点的入度与出度之差小于等于1.如不存在,输出-1:存在,则按边的输入顺序输出方向. 比如:若给出第 m 条边为 u,v,则在对应第m行输出0表示u->v,输出1表示u<-v. 解题思路: 首先证明一定可以构造出符合要求的图. 对于一个环,显然可以使得其内的每个点入度与出度相等.之后可以把环看成点. 对于一棵树,则可以通

2015 多校赛 第四场 1009 (hdu 5335)

Problem Description In an n∗m maze, the right-bottom corner is the exit (position (n,m) is the exit). In every position of this maze, there is either a 0 or a 1 written on it. An explorer gets lost in this grid. His position now is (1,1), and he want

2015 多校赛 第七场 1011 (hdu 5379)

题意:给定一棵树,树上有 n 个节点.问有多少种方案,使得在每个节点上依次放置数 1~n 后,每个节点的儿子节点上的数连续(比如 1 为根,有1-2,1-3,1-4,则令2,3,4上的数连续),每个子树上的数连续. 注释在代码里. #pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<cstdio> #include<algorithm> #

2014多校第五场1010 || HDU 4920 Matrix multiplication(矩阵乘法优化)

题目链接 题意 : 给你两个n*n的矩阵,然后两个相乘得出结果是多少. 思路 :一开始因为知道会超时所以没敢用最普通的方法做,所以一直在想要怎么处理,没想到鹏哥告诉我们后台数据是随机跑的,所以极端数据是不可能会有的,而我们一开始一直在想极端数据能接受的方法......后来看了鹏哥的做法,就是把是0的地方都跳过就可以了,用矩阵保存前一个非0数的位置是多少.二师兄给我看了一个代码,人家根本没用别的优化,直接将最里层k的循环提到了最外层,然后就AC了,对此我表示无语. 1 #include <cstd

HDOJ多校联合第五场

1001 题意:求逆序对,然后交换k次相邻的两个数,使得剩下的逆序对最少. 分析:题目用到的结论是:数组中存在一对逆序对,那么可以通过交换相邻两个数使得逆序对减少1,交换k次,可以最多减少k个. 嘉定ai>aj,i < j,如果ai,aj相邻的,那么显然可以通过交换减少1:不相邻的情况, 考虑ak,k = j-1; #11:ak > aj,那么ak,aj构成逆序对,交换后逆序对减少1: #12:ak<=aj,那么ai,ak构成逆序对,问题转化为更小规模,可以通过同样的方法进一步分析

2014多校联合-第五场

1001:Inversion 模版题,求逆序数对.有多少逆序数对,就可以剪掉多少. 1003:Least common multiple 对于每一个子集,lcm为2的a的最大值次方*3的b的最大值次方. 所以我们只需要求出以某个b为b的最大值的时候,a的最大值的分布情况即可. 我们先把b从小到大排序. 对于某一个b,我门只需要求出之前出现过的a比当前a小的数量为x: 那么就可知对于这些a的子集,为2^x个,并且,每个子集a的最大值都为当前a. 我么还需要求出对于大于当前a的a有多少个比a小的数,

HDU多校赛第9场 HDU 4965Fast Matrix Calculation【矩阵运算+数学小知识】

难度上,,,确实,,,不算难 问题是有个矩阵运算的优化 题目是说给个N*K的矩阵A给个K*N的矩阵B(1<=N<=1000 && 1=<K<=6),先把他们乘起来乘为C矩阵,然后算C^(N*N) 相当于 ABABABABABABAB...=(AB)^(N*N) 不如 A(BA)^(N*N-1)B 因为BA乘得K*K的矩阵,K是比较小的 #include <cstdio> #include <cstdlib> #include <cstr

hdu5336 多校联合第四场1010 模拟+bfs优先队列

http://acm.hdu.edu.cn/showproblem.php?pid=5336 Problem Description XYZ is playing an interesting game called "drops". It is played on a r?c grid. Each grid cell is either empty, or occupied by a waterdrop. Each waterdrop has a property "siz