poj1639,uva1537,uvalive2099,scu1622,fzu1761 Picnic Planning (最小限制生成树)

Picnic Planning

Time Limit: 5000MS   Memory Limit: 10000K
Total Submissions: 10742   Accepted: 3885

Description

The Contortion Brothers are a famous set of circus clowns, known worldwide for their incredible ability to cram an unlimited number of themselves into even the smallest vehicle. During the off-season, the brothers like to get together for an Annual Contortionists Meeting at a local park. However, the brothers are not only tight with regard to cramped quarters, but with money as well, so they try to find the way to get everyone to the party which minimizes the number of miles put on everyone‘s cars (thus saving gas, wear and tear, etc.). To this end they are willing to cram themselves into as few cars as necessary to minimize the total number of miles put on all their cars together. This often results in many brothers driving to one brother‘s house, leaving all but one car there and piling into the remaining one. There is a constraint at the park, however: the parking lot at the picnic site can only hold a limited number of cars, so that must be factored into the overall miserly calculation. Also, due to an entrance fee to the park, once any brother‘s car arrives at the park it is there to stay; he will not drop off his passengers and then leave to pick up other brothers. Now for your average circus clan, solving this problem is a challenge, so it is left to you to write a program to solve their milage minimization problem.

Input

Input will consist of one problem instance. The first line will contain a single integer n indicating the number of highway connections between brothers or between brothers and the park. The next n lines will contain one connection per line, of the form name1 name2 dist, where name1 and name2 are either the names of two brothers or the word Park and a brother‘s name (in either order), and dist is the integer distance between them. These roads will all be 2-way roads, and dist will always be positive.The maximum number of brothers will be 20 and the maximumlength of any name will be 10 characters.Following these n lines will be one final line containing an integer s which specifies the number of cars which can fit in the parking lot of the picnic site. You may assume that there is a path from every brother‘s house to the park and that a solution exists for each problem instance.

Output

Output should consist of one line of the form 
Total miles driven: xxx 
where xxx is the total number of miles driven by all the brothers‘ cars.

Sample Input

10
Alphonzo Bernardo 32
Alphonzo Park 57
Alphonzo Eduardo 43
Bernardo Park 19
Bernardo Clemenzi 82
Clemenzi Park 65
Clemenzi Herb 90
Clemenzi Eduardo 109
Park Herb 24
Herb Eduardo 79
3

Sample Output

Total miles driven: 183

题意:

要求做一个最小生成树,限制条件:给定其中一个点限制其的度不超过 k (最小 k 度限制生成树)。

思路:

第一步,设被限制度数的节点为 v0 ,则在去除 v0 的情况下做最小生成树,可能得到若干个最小生成树(设有 m 棵树);容易想到,这写树必须通过 v0 来连接成一颗树。

第二步,从 v0 引出 m 条边分别连向 m 棵树,则此时得到一个最小 m 度限制生成树,若给定的 k 小于 m 则说明这不是连通图,无法做生成树。

第三步,最多找出 k-m 条 v0 的边去替换树上现有的边;当然,替换必须使树变小才合法。这一步是比较麻烦的,并且若直接枚举的话时间复杂度也较高。每次使用动态规划找出一条贡献最大的边,并替换进树中。直到找齐 k-m 条边、或无法找到合法边是停止。此时得到的就是最小 k 度限制生成树了。

总结:

思路如上十分清晰,可实现起来细节太多了,比较坑的是同一道题不能在不同的OJ AC。在多次调试之后我的代码总算征服了poj、uva、uvalive、scu,但 fzu 却迟迟不能AC。在纵观其他大佬的题解后,发现我的代码已经算强的了....

此题需要注意的是:输入是两点之间可能存在多条边,需要保留最小的边。

代码:

#include<iostream>
#include<map>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstdio>
#define READFILE freopen("D:\\in.txt","r",stdin);
#define INF 1e9+7
using namespace std;

class Edge
{
public:
    int u, v, w;
    Edge(int a=0, int b=0, int c=0):u(a), v(b), w(c) {}
};

map<string, int> mp;
vector<Edge> edges;
Edge dp[105];
int m, n, k, md, grap[105][105], fa[105], mst[105][105], ans=0;

bool cmp(Edge a, Edge b)
{
    return a.w<b.w;
}

void Init()
{
    memset(grap, -1, sizeof(grap));//-1不可达
    memset(mst, 0, sizeof(mst));
    mp.clear();
    edges.clear();
    n=1, md=0, ans=0, k=0;
    int u, v, w;
    mp["Park"]=1;

    string name1, name2;
    cin>>m;
    for(int i=0; i<m; ++i)
    {
        cin>>name1>>name2>>w;
        if(mp.find(name1)==mp.end())
            mp[name1]=n++;
        if(mp.find(name2)==mp.end())
            mp[name2]=n++;
        u=mp[name1], v=mp[name2];
        edges.push_back(Edge(u, v, w));
        if(grap[u][v]==-1)
            grap[u][v]=grap[v][u]=w;
        else
            grap[u][v]=grap[v][u]=min(grap[u][v], w);
    }
    cin>>k;
}

int Find(int x)
{
    if(fa[x]!=x)
        return fa[x]=Find(fa[x]);
    return x;
}

void Union(int a, int b)
{
    int x=Find(a);
    int y=Find(b);
    if(x!=y)
        fa[x]=y;
}

int Kruskal()//去除限制点生成md棵最小生成树
{
    int res=0;
    sort(edges.begin(), edges.end(), cmp);
    for(int i=0; i<=n; ++i)
        fa[i]=i;
    for(int i=0; i<edges.size(); ++i)
    {
        Edge& e=edges[i];
        if(e.u==1 || e.v==1 || Find(e.u)==Find(e.v)) continue;
        Union(e.u, e.v);
        mst[e.u][e.v]=mst[e.v][e.u]=grap[e.u][e.v];
        res+=grap[e.u][e.v];
    }
    return res;
}

int mmst()//生成最小md度限制生成树
{
    int minw[25], minv[25], res=0;
    for(int i=0; i<=n; ++i) minw[i]=INF;
    for(int i=2; i<=n; ++i)
        if(grap[1][i]!=-1)
        {
            int x=Find(i);
            if(minw[x] > grap[1][i])
            {
                minw[x]=grap[1][i];
                minv[x]=i;
            }
        }
    for(int i=1; i<=n; ++i)
        if(minw[i]!=INF)
        {
            md++;
            mst[1][minv[i]]=mst[minv[i]][1]=1;
            res+=grap[1][minv[i]];
        }
    return res;
}

void dfs(int x,int fa)
{
    for(int i=2; i<=n; i++)
        if(mst[x][i] && i!=fa)
        {
            if(dp[i].w==-1)
            {
                if(grap[x][i]<dp[x].w)
                {
                    dp[i].u=dp[x].u;
                    dp[i].v=dp[x].v;
                    dp[i].w=dp[x].w;
                }
                else
                    dp[i].u=x,dp[i].v=i,dp[i].w=grap[x][i];
            }
            dfs(i,x);
        }
}
int mkst()
{
    int res=0;
    for(int i=md+1; i<=k; i++)
    {
        for(int j=0; j<=n; ++j)
            dp[j].w=-1;
        dp[1].w=-INF;
        for(int j=2; j<=n; j++)
            if(mst[1][j])
                dp[j].w=-INF;
        dfs(1,-1);
        int t=0,minn=INF;
        for(int j=2; j<=n; j++)
            if(grap[1][j]!=-1&&grap[1][j]-dp[j].w<minn)
            {
                minn=grap[1][j]-dp[j].w;
                t=j;
            }
        if(minn>=0)
            break;
        mst[1][t]=mst[t][1]=1;
        int x=dp[t].u,y=dp[t].v;
        mst[x][y]=mst[y][x]=0;
        res+=minn;
    }
    return res;
}

int main()
{
    //READFILE
    int t;
    t=1;//有的oj多组数据此处改为cin>>t即可
    while(t--)
    {
        Init();
        int ans1=Kruskal();
        int ans2=mmst();
        int ans3=mkst();
        ans=ans1+ans2+ans3;
        cout<<"Total miles driven: "<<ans<<endl;
        if(t)cout<<endl;
    }
    return 0;
}
时间: 2024-11-06 13:07:53

poj1639,uva1537,uvalive2099,scu1622,fzu1761 Picnic Planning (最小限制生成树)的相关文章

poj1639 Picnic Planning 最小度数限制生成树

题意:若干个人开车要去park聚会,但是park能停的车是有限的,为k.所以这些人要通过先开车到其他人家中,停车,然后拼车去聚会.另外,车的容量是无限的,他们家停车位也是无限的.求开车总行程最短. 就是求一最小生成树,但是对于其中一个点其度不能超过k. 思路: 1. 将park点取出 将剩下的点求出最小生成树  出现i个联通块 2. 再每个块中选择与park点相邻的最小边 到此park点已经连了i条边 park点最大可以连接k个点 得到Sum值 3. 需要求出i+1-->k 条的Sum值 每次添

POJ 1861 ——Network——————【最小瓶颈生成树】

Network Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 15268   Accepted: 5987   Special Judge Description Andrew is working as system administrator and is planning to establish a new network in his company. There will be N hubs in the c

BZOJ2180: 最小直径生成树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2180 2180: 最小直径生成树 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 195  Solved: 93[Submit][Status][Discuss] Description 输入一个无向图G=(V,E),W(a,b)表示边(a,b)之间的长度,求一棵生成树T,使得T的直径最小.树的直径即树的最长链,即树上距离最远的两点之间路径

HDU5697 刷题计划 dp+最小乘积生成树

分析:就是不断递归寻找靠近边界的最优解 学习博客(必须先看这个): 1:http://www.cnblogs.com/autsky-jadek/p/3959446.html 2:http://blog.csdn.net/u013849646/article/details/51524748 注:这里用的最小乘积生成树的思想,和dp结合 每次找满足条件的最优的点,只不过BZOJ裸题的满足条件是形成一棵树 这个题是大于m,生成树借用最小生成树进行求解最优,大于m用dp进行求解最优 #include

算法笔记_164:算法提高 最小方差生成树(Java)

目录 1 问题描述 2 解决方案   1 问题描述 问题描述 给定带权无向图,求出一颗方差最小的生成树. 输入格式 输入多组测试数据.第一行为N,M,依次是点数和边数.接下来M行,每行三个整数U,V,W,代表连接U,V的边,和权值W.保证图连通.n=m=0标志着测试文件的结束. 输出格式 对于每组数据,输出最小方差,四舍五入到0.01.输出格式按照样例. 样例输入 4 51 2 12 3 23 4 24 1 12 4 34 61 2 12 3 23 4 34 1 12 4 31 3 30 0 样

bzoj2395[Balkan 2011]Timeismoney最小乘积生成树

所谓最小乘积生成树,即对于一个无向连通图的每一条边均有两个权值xi,yi,在图中找一颗生成树,使得Σxi*Σyi取最小值. 直接处理问题较为棘手,但每条边的权值可以描述为一个二元组(xi,yi),这也就不难想到将生成树转化为平面内的点,x代表Σxi,y代表Σyi(注意这里的xi,yi指的是在生成树中的边的权值),那么问题就变成了在平面内找一个点使得x*y最小,那么显然这个点是在下凸壳上的. 因此可以首先找出两个一定在凸包上的点,例如A(minx,y),B(miny,x),在直线AB下方找一个在凸

【最小瓶颈生成树】【最小生成树】【kruscal】bzoj1083 [SCOI2005]繁忙的都市

本意是求最小瓶颈生成树,但是我们可以证明:最小生成树也是最小瓶颈生成树(其实我不会).数据范围很小,暴力kruscal即可. 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 struct Edge{int u,v,w;void Read(){scanf("%d%d%d",&u,&v,&w);}}edges[10001]; 5 bool operator &

(最小生成树/最小瓶颈生成树) 2017武汉现场赛 - Wifi Relay

题意: n个无线AP,有xy坐标属性,现在n个无线AP要桥接在一起不能断开连接,现在要求无线AP无线网络的覆盖半径最小是多少 分析: 看起来是像是最小生成树,这里是是求生成树中最长的边最短,就是最小瓶颈生成树. 可以证明最小瓶颈生成树就是最小生成树,详细看刘汝佳<算法入门经典训练指南>343页. 当时现场的时候,想试试最小生成树了,结果以为n方复杂度过不去,就没写,现在想起来,真是坑哦. 这题n有10000,所以不能直接建邻接矩阵,而是采用动态计算距离就行了. 比赛结束了随便一写就A了...

poj 3522 Slim Span 最大边减最小边最小的生成树

枚举最小边进行kruskal. #include <cstdio> #include <algorithm> using namespace std; #define maxn 120 #define maxm 10000 struct edge { int u,v,w; }e[maxm]; int p[maxn],n,m; int find(int x) { if(x==p[x]) return x; return p[x]=find(p[x]); } void link(int