贪心,暴力,网络流,匹配(MZL's City,HDU 5352)

来源:互联网 发布:程序员常用工具 知乎 编辑:程序博客网 时间:2024/06/07 14:17

这个题目可以抽象成以下问题。

有一个M层的架子,每层最多放K颗珍珠,你有N个不同的珍珠,每颗珍珠都只能放在某些特定的层里,问怎么安排放置的方案,使得珍珠尽量往下层放(尽量往下层放的概念很模糊,准确一点讲就是字典序最小)。


思考之后觉得这是一个很动态的问题,考虑过动态规划,但是感觉不怎么好定义状态,更别提状态转移了。然后就考虑转贪心,思考了很多办法,最后想到了一个暴力的方法,能保证正确性,但是时间复杂度不是很好估计(最坏情况铁定超时,但是感觉这个上界非常松),所以决定试一下,然后就过了,而且时间还比常规解法快很多。


具体解法就是类似模拟,先把所有珍珠放在最顶层(最顶层是一个虚层,代表无限高的层)。然后按照从上到下的顺序遍历每个层的每个珍珠,然后尝试把它往下放。

如果下层还有多余空间,我们就直接往下放。

否则我们就要考虑将另一颗珍珠“置换”上去。

A把B“置换”上去的意思就是,将A放到B的位置,然后把B放到一个更高但是又比A原来的位置更低的位置。


这样的解法能够保证正确性,但是时间复杂度很不好估计,最坏情况下就是O(M*N*M*N*M*logN),肯定超时的,而且还没有考虑set的常数。但是能明显感到这个上界是非常松的,自己大致估计了一下平均情况,感觉搞不好真的可以过,所以就试了一下,然后就过了。


关于正确性,因为我们是从上往下遍历,能优化就优化,实在优化不了才跳过。因此当算法结束以后,我们无法再通过任何变动使得答案更优了。


关于其它解法,看了网上的解法,学习了一下,自己没有亲自实现,但还是想写一写理解。

主要看了这篇博客:http://blog.csdn.net/fipped/article/details/47305699

1、最小费用最大流

这个是最好理解的方法,但其他方法其实也就是这个方法的特例。

建立N个点代表城市,建立M个点代表年份。

建立一个超级汇点,所有城市都连一条容量为1,费用为0的边到汇点。

建立一个超级源点,对每个年份都连一条容量为K的边,费用为M+1-年份。

每个年份都对可以建造的城市连一条容量为1,费用为0的边。

然后跑最小费用最大流,得到的最大流就是最多的城市数。

考虑每一个年份节点即可知道对应年份所建立的城市数。

为什么源点与年份之间的边花费为M+1-年份呢?

主要原因是因为这个是最大流下的最小费,而不是单纯的最小费。

如果是单纯的最小费,这个花费的设置可能会导致问题,应该设成210^(M+1-年份)才合理,只有设成这个值才能代表前一年的无穷大。

但是由于满足最大流的限制,因此备选的方案的流量应该都是相等的,在这种情况下只需要设成M+1-年份就一定可以通过求最小费得出最优解。

2、最大流

没有使用最小费,只是单纯的多次跑最大流。

讲的更具体一点:

建立N个点代表城市。

建立一个超级汇点,所有城市都连一条容量为1的边到汇点。

然后逐年地动态建图,并跑一次最大流。

即按降序考虑每一个年份,对于一个特定的年份,新建一个节点代表当年,并对所有可以建立的城市都连一条容量为1的边。

然后从源点连一条容量为K的边到当年的节点,然后跑一次最大流。

每年新增的流量就是当年建造的城市量,加起来就是总城市量。

为什么这个算法是正确的呢?
我们在紫书P371上可以看到关于最小费用流的一句话。

只要初始流是该流量下的最小费用可行流,每次增广后的新流都是新流量下的最小费用流。

最小费用最大流的算法就是基于这句话来实现的,Edmonds-Karp算法就是每次沿着最短路进行增广,多次增广直到无法再增加流量,因此算法结束后得到的最大流一定是最小费的。

本题的最大流解法其实本质上也就是最小费用最大流解法。

按照降序考虑每一个年份跑最大流,其实就等价于每次沿着最短路进行增广,因此最后得到的结果也就是正确的了。

3、匹配

我们都知道,最大流可以解决匹配问题,但匹配问题不一定能解决最大流问题。

但在本题中,我们可以用匹配来代替最大流,同样可以得到答案。

代替的方式就是将一个年份拆成K个点,然后跑匈牙利算法,这同时也是解决多重匹配问题的一个办法。

其他细节和思路可以参考2。

关于多重匹配:http://blog.csdn.net/allenjy123/article/details/6674118


上面三种方法都没有亲自实现,可能思路或理解会有错误,仅供参考。


贪心暴力的代码

#include<stdio.h>#include<set>#include<string.h>#include<vector>#include<algorithm>using namespace std;const int maxn = 210;const int maxc = 510;int N,M,K;set<int>G[maxn];set<int>ceng[maxc];vector<int>vec[maxn];int vis[maxn];int cntceng;void init(){    scanf("%d %d %d",&N,&M,&K);    ceng[0].clear();    for(int i=1;i<=N;i++) ceng[0].insert(i),G[i].clear(),vec[i].clear();    cntceng=1;}void dfs(int u){    vis[u]=1;    vec[u].push_back(cntceng-1);    for(set<int>::iterator it = G[u].begin();it!=G[u].end();++it)    {        int v = *it;        if(!vis[v]) dfs(v);    }}void build(){    int op,u,v,p;    for(int i=1;i<=M;i++)    {        scanf("%d",&op);        if(op==2)        {            scanf("%d %d",&u,&v);            G[u].insert(v);            G[v].insert(u);        }        else if(op==3)        {            scanf("%d",&p);            for(int j=1;j<=p;j++)            {                scanf("%d %d",&u,&v);                G[u].erase(v);                G[v].erase(u);            }        }        else        {            scanf("%d",&p);            memset(vis,0,sizeof(vis));            ceng[cntceng++].clear();            dfs(p);        }    }}void solve(){    init();    build();    for(int i=0;i<cntceng;i++)    {        for(set<int>::iterator it = ceng[i].begin();it!=ceng[i].end();)        {            bool ok = false;            for(int j=(int)vec[*it].size()-1;j>=0&&vec[*it][j]>i;j--)            {                int CENG = vec[*it][j];                if((int)ceng[CENG].size()<K)                {                    ok=true;                    ceng[CENG].insert(*it);                    ceng[i].erase(*it++);                    break;                }                for(set<int>::iterator it2 = ceng[CENG].begin();it2!=ceng[CENG].end();++it2)                {                    if(*it2!=*it)                        for(int k = (int)vec[*it2].size()-1;k>=0&&vec[*it2][k]>i;--k)                        {                            //printf("%d\n",vec[*it2][k]);                            //printf("%d\n",ceng[vec[*it2][k]].size());                            if(vec[*it2][k]<CENG&&(int)ceng[vec[*it2][k]].size()<K)                            {                                ok=true;                                ceng[vec[*it2][k]].insert(*it2);                                ceng[CENG].erase(*it2);                                ceng[CENG].insert(*it);                                ceng[i].erase(*it++);                                break;                            }                        }                    if(ok) break;                }                if(ok) break;            }            if(!ok) ++it;        }    }    printf("%d\n",N-ceng[0].size());    for(int i=1;i<cntceng;i++) printf("%d%c",ceng[i].size(),i+1==cntceng?'\n':' ');}int main(){    int T;    scanf("%d",&T);    while(T--) solve();    return 0;}