【POJ 1639】 Picnic Planning (最小k度限制生成树)
【题意】
有n个巨人要去Park聚会。巨人A和先到巨人B那里去,然后和巨人B一起去Park。B君是个土豪,他家的停车场很大,可以停很多车,但是Park的停车场是比较小。只能停k辆车。现在问你在这个限制条件下。巨人到达Park的最短距离。
如果把那个条件去掉。那么就是就是求最小生成树。加上那个条件其实就是求顶点度数限制为k的最小生成树。
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
【分析】
其实我还没A,233,先打着题解。
题意也没看懂,反正就求最小k度生成树吧。
感觉这种生成树都有自己的方法,跟kruskal没什么太大关系,只是里面要用到一下,要先求出去点根的MST。
/*************************************************
算法引入:
最小k度限制生成树,就是指有特殊的某一点的度不能超过k时的最小生成树;
如果T是G的一个生成树且dT(v0)=k,则称T为G的k度限制生成树;
G中权值和最小的k度限制生成树称为G的最小k度生成树;算法思想:
设特殊的那点为v0,先把v0删除,求出剩下连通图的所有最小生成树;
假如有m棵最小生成树,那么这些生成树必定要跟v0点相连;
也就是说这棵生成树的v0点至少是m度的;
若m>k,条件不成立,无法找到最小k度限制生成树;
若m<=k,则枚举m到k的所有最小生成树,即一步步将v0点的度加1,直到v0点的度为k为止;
则v0点度从m到k的(k-m+1)棵最小生成树中最小的那棵即为答案;算法步骤:
(1)先求出最小m度限制生成树:
原图中去掉和V0相连的所有边(可以先存两个图,建议一个邻接矩阵,一个邻接表,用方便枚举边的邻接表来构造新图);
得到m个连通分量,则这m个连通分量必须通过v0来连接;
则在图G的所有生成树中dT(v0)>=m;
则当k<m时,问题无解;
对每个连通分量求一次最小生成树;
对于每个连通分量V’,用一条与V0直接连接的最小的边把它与V0点连接起来,使其整体成为一个生成树;
就得到了一个m度限制生成树,即为最小m度限制生成树;(2)由最小m度限制生成树得到最小m+1度限制生成树;
连接和V0相邻的点v,则可以知道一定会有一个环出现(因为原来是一个生成树);
只要找到这个环上的最大权边(不能与v0点直接相连)并删除,就可以得到一个m+1度限制生成树;
枚举所有和V0相邻点v,找到替换后,增加权值最小的一次替换(如果找不到这样的边,就说明已经求出);
就可以求得m+1度限制生成树;
如果每添加一条边,都需要对环上的边一一枚举,时间复杂度将比较高;
用动态规划解决;
设dp(v)为路径v0—v上与v0无关联且权值最大的边;
定义father(v)为v的父结点,由此可以得到状态转移方程:
dp(v)=max(dp(father(v)),ω(father(v),v));
边界条件为dp[v0]=-∞(因为每次寻找的是最大边,所以-∞不会被考虑),dp[v’]=-∞|(v0,v’)∈E(T);(3)当dT(v0)=k时停止(即当V0的度为k的时候停止),但不一定k的时候最优;
算法实现:
并查集+kruskal;
首先,每个连通分量的的最小生成树可以直接用一个循环,循环着Kruskal求出;
这里利用了联通分量间的独立性,对每个连通分量分别求最小生成树,和放在一起求,毫不影响;
而且kruskral算法保证了各连通分量边的有序性;
找最小边的时候,可以用动态规划,也可以这么做:
先走一个循环,但我们需要逆过来加边,将与v0关联的所有边从小到达排序;
然后将各连通分量连接起来,利用并查集可以保证每个连通分量只有一条边与v0相连;
由于边已经从小到达排序,故与每个连通分量相连的边就是每个连通分量与v0相连中的最小边;
然后求m+1度的最小生成树时,可以直接用DFS,最小生成树要一直求到k度,然后从中找出一个最优值;
好吧AC了,度数可以小于等于k的哦~~
我还以为要把小于等于k的全部求出来然后min,其实有个地方可以直接break(看代码!!)
- #include<cstdio>
- #include<cstdlib>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<map>
- #include<string>
- using namespace std;
- #define INF 0xfffffff
- #define Maxn 310
- struct node
- {
- int x,y,c;
- }t[Maxn*Maxn],dp[Maxn];
- bool cmp(node x,node y) {return x.c<y.c;}
- map<string,int> M;
- bool flag[Maxn][Maxn];
- int G[Maxn][Maxn],fa[Maxn];
- int cnt;
- int get_num(string s)
- {
- if(M.find(s)==M.end())
- {
- M[s]=++cnt;
- }
- return M[s];
- }
- int ffa(int x)
- {
- if(fa[x]!=x) fa[x]=ffa(fa[x]);
- return fa[x];
- }
- int ans,n,m;
- void init()
- {
- cnt=;ans=;
- M["Park"]=;
- memset(flag,,sizeof(flag));
- memset(G,-,sizeof(G));
- scanf("%d",&n);
- for(int i=;i<Maxn;i++) fa[i]=i;
- string s;
- for(int i=;i<=n;i++)
- {
- cin>>s;
- t[i].x=get_num(s);
- cin>>s;
- t[i].y=get_num(s);
- scanf("%d",&t[i].c);
- if(G[t[i].x][t[i].y]==-)
- G[t[i].x][t[i].y]=G[t[i].y][t[i].x]=t[i].c;
- else
- G[t[i].x][t[i].y]=G[t[i].y][t[i].x]=min(G[t[i].y][t[i].x],t[i].c);
- }
- scanf("%d",&m);
- }
- void kruskal()
- {
- sort(t+,t++n,cmp);
- int ct=;
- for(int i=;i<=n;i++)
- {
- if(t[i].x==||t[i].y==) continue;
- if(ffa(t[i].x)!=ffa(t[i].y))
- {
- fa[ffa(t[i].x)]=ffa(t[i].y);
- ct++;
- flag[t[i].x][t[i].y]=flag[t[i].y][t[i].x]=;
- ans+=t[i].c;
- }
- // if(ct==cnt-2) break;
- }
- }
- void dfs(int x,int f)
- {
- for(int i=;i<=cnt;i++) if(i!=f&&flag[x][i])
- {
- if(!flag[][i])
- {
- if(dp[x].c>G[x][i]) dp[i]=dp[x];
- else dp[i].c=G[x][i],dp[i].x=x,dp[i].y=i;
- }
- dfs(i,x);
- }
- }
- int mn[Maxn],tmp[Maxn];
- void solve()
- {
- kruskal();
- for(int i=;i<=cnt;i++) mn[i]=INF;
- for(int i=;i<=cnt;i++) if(G[][i]!=-)
- {
- int r=ffa(i);
- if(mn[r]>G[][i])
- {
- tmp[r]=i;
- mn[r]=G[][i];
- }
- }
- int r=;
- for(int i=;i<=cnt;i++) if(mn[i]!=INF)
- {
- r++;
- flag[][tmp[i]]=flag[tmp[i]][]=;
- ans+=G[][tmp[i]];
- }
- for(int i=r+;i<=m;i++)
- {
- dp[].c=-INF;
- for(int j=;j<=cnt;j++) if(flag[][j]) dp[j].c=-INF;
- dfs(,);
- int tmp,minn=INF;
- for(int j=;j<=cnt;j++) if(G[][j]!=-)
- {
- if(minn>G[][j]-dp[j].c)
- {
- minn=G[][j]-dp[j].c;
- tmp=j;
- }
- }
- if(minn>=) break;
- flag[][tmp]=flag[tmp][]=;
- int x=dp[tmp].x,y=dp[tmp].y;
- flag[x][y]=flag[y][x]=;
- ans+=minn;
- }
- printf("Total miles driven: %d\n",ans);
- }
- int main()
- {
- init();
- solve();
- return ;
- }
放一个有解释的代码,我就是看着他打的。。
- #include<iostream>
- #include<string>
- #include<cstdio>
- #include<map>
- #include<cstring>
- #include<algorithm>
- using namespace std;
- const int INF=;
- const int N=;
- int n,m;//n为边的数量,m表示限度值
- int cnt;//计算出来的结点数
- int set[N];
- bool flag[N][N];
- int G[N][N];
- int ans;
- map<string,int> Map;
- struct node
- {
- int x,y,v;
- } a[N*N];
- struct edge
- {
- int x,y,v;
- } dp[N];
- int get_num(string s)//返回每个人对应结点
- {
- if(Map.find(s)==Map.end())//没有搜索到该键值
- {
- Map[s]=++cnt;//对应建图
- }
- // cout<<" Map["<<s<<"]=="<<Map[s]<<endl;
- return Map[s];
- }
- bool cmp(node a,node b)
- {
- return a.v<b.v;
- }
- int find_set(int x)
- {
- if(x!=set[x])
- set[x]=find_set(set[x]);
- return set[x];
- }
- inline void union_set(int x,int y)
- {
- set[y]=x;
- }
- void kruskal()//求m个连通分量的最小生成树
- {
- for(int i=; i<=n; i++)
- {
- if(a[i].x==||a[i].y==)
- continue;
- int x=find_set(a[i].x);
- int y=find_set(a[i].y);
- if(x==y)
- continue;
- flag[a[i].x][a[i].y]=flag[a[i].y][a[i].x]=true;
- set[y]=x;
- ans+=a[i].v;
- }
- }
- void dfs(int x,int fa)
- {
- for(int i=; i<=cnt; i++)
- if(i!=fa&&flag[x][i])
- {
- if(dp[i].v==-)
- {
- if(dp[x].v>G[x][i])//dp(v)=max(dp(father(v)),ω(father(v),v));
- {
- dp[i]=dp[x];
- }
- else
- {
- dp[i].v=G[x][i];
- dp[i].x=x;
- dp[i].y=i;
- }
- }
- dfs(i,x);
- }
- }
- void init()
- {
- ans=;
- cnt=;
- Map["Park"]=;
- memset(flag,,sizeof(flag));
- memset(G,-,sizeof(G));
- scanf("%d",&n);
- for(int i=; i<N; i++)//并查集初始化
- set[i]=i;
- string s;
- for(int i=; i<=n; i++)
- {
- cin>>s;
- a[i].x=get_num(s);
- cin>>s;
- a[i].y=get_num(s);
- cin>>a[i].v;
- if(G[a[i].x][a[i].y]==-)
- G[a[i].x][a[i].y]=G[a[i].y][a[i].x]=a[i].v;
- else//有重边
- G[a[i].x][a[i].y]=G[a[i].y][a[i].x]=min(G[a[i].y][a[i].x],a[i].v);
- }
- scanf("%d",&m);//m表示限度值
- }
- void solve()
- {
- int tmp[N],Min[N];
- for(int i=; i<=cnt; i++)
- Min[i]=INF;
- sort(a+,a++n,cmp);
- kruskal();
- for(int i=; i<=cnt; i++)
- {
- if(G[][i]!=-)
- {
- int t=find_set(i);
- if(Min[t]>G[][i])//求每个连通分量中和顶点1连接的最小权边
- {
- tmp[t]=i;
- Min[t]=G[][i];
- }
- }
- }
- int t=;//t表示最小限度
- for(int i=; i<=cnt; i++)
- if(Min[i]!=INF)
- {
- t++;
- flag[][tmp[i]]=flag[tmp[i]][]=true;
- ans+=G[][tmp[i]];
- }
- for(int i=t+; i<=m; i++)//枚举t到m的所有最小生成树,即一步步将v1点的度加1,直到v1点的度为m为止;
- {
- memset(dp,-,sizeof(dp));//dp[v]为路径v0—v上与v0无关联且权值最大的边;
- dp[].v=-INF;
- for(int j=; j<=cnt; j++)
- if(flag[][j])
- dp[j].v=-INF;
- dfs(,-);
- int tmp,Min=INF;
- for(int j=; j<=cnt; j++)
- if(G[][j]!=-)
- {
- if(Min>G[][j]-dp[j].v)
- {
- Min=G[][j]-dp[j].v;
- tmp=j;
- }
- }
- if(Min>=)//找不到这样的边,就说明已经求出
- break;
- flag[][tmp]=flag[tmp][]=true;
- int x=dp[tmp].x;
- int y=dp[tmp].y;
- flag[x][y]=false;
- flag[y][x]=false;
- ans+=Min;
- printf("%d\n",ans);
- }
- printf("Total miles driven: %d\n",ans);
- }
- int main()
- {
- init();
- solve();
- return ;
- }
别人家的code
2016-11-03 08:45:50
【POJ 1639】 Picnic Planning (最小k度限制生成树)的更多相关文章
- POJ 1639 Picnic Planning 最小k度生成树
Picnic Planning Time Limit: 5000MS Memory Limit: 10000K Total Submissions:11615 Accepted: 4172 D ...
- poj1639 Picnic Planning,K度限制生成树
题意: 矮人虽小却喜欢乘坐巨大的轿车,车大到能够装下不管多少矮人.某天,N(N≤20)个矮人打算到野外聚餐.为了集中到聚餐地点,矮人A 要么开车到矮人B 家中,留下自己的轿车在矮人B 家,然后乘坐B ...
- POJ 1639 Picnic Planning:最小度限制生成树
题目链接:http://poj.org/problem?id=1639 题意: 给你一个无向图,n个节点,m条边,每条边有边权. 让你求一棵最小生成树,同时保证1号节点的度数<=k. 题解: 最 ...
- POJ 1639 Picnic Planning(最小度限制生成树)
Description The Contortion Brothers are a famous set of circus clowns, known worldwide for their inc ...
- poj 1639 最小k度限制生成树
题目链接:https://vjudge.net/problem 题意: 给各位看一下题意,算法详解看下面大佬博客吧,写的很好. 参考博客:最小k度限制生成树 - chty - 博客园 https:/ ...
- 最小k度限制生成树
[题目描述] 给你一个图,n个点,m条边,求一颗生成树满足如下条件: (1)结点1的度不超过k. (2)在(1)条件下所求生成树最小. [算法引入] 最小k度限制生成树,就是指有特殊的某一点的度不能超 ...
- poj 1639 Picnic Planning 度限制mst
https://vjudge.net/problem/POJ-1639 题意: 有一群人,他们要去某一个地方,每个车可以装无数个人,给出了n条路,包含的信息有路连接的地方,以及路的长度,路是双向的,但 ...
- [POJ 1639] Picnic Planning
[题目链接] http://poj.org/problem?id=1639 [算法] 首先,我们可以用深度优先遍历求出1号节点去除后有几个联通块 设共有T个联通块,若T > K则无解,否则 : ...
- poj1639,uva1537,uvalive2099,scu1622,fzu1761 Picnic Planning (最小限制生成树)
Picnic Planning Time Limit: 5000MS Memory Limit: 10000K Total Submissions: 10742 Accepted: 3885 ...
随机推荐
- C++学习(一)
一.C++语言语法基础(6)1.从C到C++的过渡(1)2.类和对象(2)剑3.操作符重载(1)4.继承与多态(1)5.异常和I/O流(1)二.数据结构和算法(3)1.基本数据结构,堆栈.队列.链表. ...
- view,SurfaceView,GLSurfaceView的关系和区别
如果你的游戏不吃CPU,用View就比较好,符合标准Android操作方式,由系统决定刷新surface的时机. 但如果很不幸的,你做不到不让你的程序吃CPU,你就只好使用SurfaceView来强制 ...
- 关于MDCSwipeToChooseView的应用
本人因为项目中某个页面的功能需要,用到了MDCSwipeToChooseView,就在网上查阅了相关的资料,资源有很多,但应该都是同一个人上传的,code4还有git上都有,但下载demo下来后运行不 ...
- 定位相关-CLLocationManager的使用。
#import "ViewController.h" #import <CoreLocation/CoreLocation.h> @interface ViewCont ...
- ios专题 - 委托模式实现
在ios中,委托模式非常常见,那委托模式是什么? 委托模式是把一个对象把请求给另一个对象处理. 下面见例子: #import <UIKit/UIKit.h> @protocol LQIPe ...
- 暑假集训(1)第一弹 -----士兵队列训练问题(Hdu1276)
Description 某部队进行新兵队列训练,将新兵从一开始按顺序依次编号,并排成一行横队,训练的规则如下:从头开始一至二报数,凡报到二的出列,剩下的向小序号方向靠拢,再从头开始进行一至三报数,凡报 ...
- [翻译]ASP.NET Web API 2入门
原文:Getting Started with ASP.NET Web API 2 Step 1:新建一个Empty的Web API Project. Step 2:添加一个Model: public ...
- linux中python环境搭建及升级后yum不可用解决方案
1.1 LinuxCentOS 为例.1.1.1 升级 Python(1) 下载 Python 版本$ wget https://www.python.org/ftp/python/2.7.11/Py ...
- Git---Git及GitHub使用笔记
一.远程项目获取(克隆) syntax: $ git clone <版本库的网址> $ git clone <版本库的网址> <本地目录名> example: $ ...
- WebStorm快捷键收集
1.webstorm快捷键: IntelliJ-Idea 的快捷键 Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者/*…*/ ) Shift+F6 重构-重命名 Ctrl+X 删除行 C ...