Problem Address:http://poj.org/problem?id=1639
【前言】
一道10Y的题目,7WA+2RE。
刚开始确实不知道是这种东西。
后来看了discuss。
后来又下了官方数据。
昨天提交了九次。
到最后熄灯休息。
今天起来A掉。
不过最后一个错误明明是之前有写了的,
不知道怎么就不见了。
【思路】
参考:
(1)http://happylch21.blog.163.com/blog/static/16563975920113224491211/
(2)http://wenku.baidu.com/view/f186b1daa58da0116c1749ac.html
(3)http://hi.baidu.com/czyuan_acm/blog/item/5b832926f9910f108a82a1fb.html
度限制生成树是NP Hard问题,但是由于这道题只限制一个点,所以可解。
下面介绍计算过程:
(1)把根结点v0去掉,同时去掉跟其相连的边。
(2)计算此时的连通个数m,并分别求出每个连通分量的最小生成树。
(3)计算每个连通分量到v0的最小权值边,并将此边添加进来,于是添加了m条边。
(4)限制度为k。如果k==m,说明此时已满足条件,退出。
(5)如果k<m,说明无解,退出。
(6)如果k>m,则接下来就是要从m度增加到m+1度。
(7)查看所有不在当前树中的与v0有关联的边,计算:如果将其添加进来,必定存在一个环,计算此环上与v0不相连的最大权值的边,并计算该边减去与环相连的边的值(可用DP优化)。找出所有这样的最大值,找出其中最大的,将该边从当前树中去掉,并把该条与v0相连的边添加到当前树中。
(8)如果已无法找到边,或者找到的边j减去连边的值小于等于零,即即使添加了权值总和也不会减少,则退出。
(9)最终得到k度限制最小生成树。
顺便介绍次小生成树:
求出最小生成树。
枚举所有不在树中的边,如果将其添加进来则必定存在一个环,找出环上的最大权值边,并计算连边与该边的差。
找出所有这样结果中的最小值,并把相应的边删去而把连边添加进来。
便可得到次小生成树。
【代码】
能力不足,代码200+,而且似乎无优化= =
#include <iostream>
#include <map>
#include <string>
using namespace std;
const int maxn = 20;
const int MAX = 99999999;
int g[maxn+5][maxn+5];
int f[maxn+5][maxn+5];
int visited[maxn+5];
int edge[maxn+5][maxn+5];
struct detail
{
int u;
int v;
int w;
int jv;
int jw;
int add;
};
void prim(int v, int n, int root, int &sum)
{
int i, j;
int temp, tb, tp;
int d[maxn+5];
int ct = 1;
d[0] = v;
visited[v] = 1;
while(1)
{
temp = MAX;
for (i=0; i<ct; i++)
{
for (j=0; j<n; j++)
{
if (visited[j]==0 && f[d[i]][j]<temp)
{
temp = f[d[i]][j];
tb = d[i];
tp = j;
}
}
}
if (temp==MAX) break;
sum += temp;
visited[tp] = 1;
d[ct] = tp;
ct++;
edge[tb][tp] = edge[tp][tb] = 1;
}
temp = MAX;
for (i=0; i<ct; i++)
{
if (g[root][d[i]]<temp)
{
temp = g[root][d[i]];
tp = d[i];
}
}
f[root][tp] = f[tp][root] = temp;
edge[root][tp] = edge[tp][root] = 1;
sum += temp;
}
int q[maxn*maxn];
int pre[maxn*maxn];
detail bfs(int v, int root, int n)
{
detail temp;
int i;
int t;
int head = 1;
int tail = 0;
q[0] = v;
int sign[maxn+5] = {0};
sign[v] = 1;
pre[v] = -1;
while(head>tail)
{
t = q[tail];
tail++;
for (i=0; i<n; i++)
{
if (sign[i]==0 && edge[t][i]==1 && f[t][i]<MAX)
{
sign[i] = 1;
pre[i] = t;
q[head] = i;
head++;
if (i==root)
break;
}
}
if (i<n) break;
}
if (head==tail)
{
temp.add = -MAX + 1;
}
else
{
temp.w = -MAX;
while(pre[i]!=-1)
{
if (f[i][pre[i]]>temp.w)
{
temp.w = f[i][pre[i]];
temp.u = pre[i];
temp.v = i;
}
i = pre[i];
}
temp.jv = v;
temp.jw = g[v][root];
temp.add = temp.w - temp.jw;
}
return temp;
}
int MST(int n, int v, int k)
{
int sum = 0;
int i, j;
for (i=0; i<n; i++)
{
for (j=0; j<n; j++)
{
f[i][j] = g[i][j];
edge[i][j] = 0;
}
}
for (i=0; i<n; i++)
f[v][i] = f[i][v] = MAX;
for (i=0; i<n; i++)
visited[i] = 0;
visited[v] = 1;
int ct = 0;
for (i=0; i<n; i++)
{
if (visited[i]==0)
{
prim(i, n, v, sum);//查找连通量
ct++;
}
}
detail temp, md;
for (i=ct; i<k; i++)//循环增删边
{
md.add = -MAX;
for (j=0; j<n; j++)
{
if (edge[v][j]==0 && g[v][j]<MAX)
{
temp = bfs(j, v, n);//查找最大权值边
if (temp.add>md.add)
md = temp;
}
}
if (md.add<=0) break;//如果无法再减少权值和则退出
f[v][md.jv] = f[md.jv][v] = g[v][md.jv];
edge[v][md.jv] = edge[md.jv][v] = 1;
f[md.u][md.v] = f[md.v][md.u] = MAX;
edge[md.u][md.v] = edge[md.v][md.u] = 0;
sum -= md.add;
}
return sum;
}
int main()
{
map<string,int>m;
int n;
int i, j;
int s;
string x, y;
int tx, ty;
int dist;
m.clear();
for (i=0; i<=maxn; i++)
{
for (j=0; j<=maxn; j++)
g[i][j] = MAX;
}
cin>>n;
for (i=0; i<n; i++)
{
cin>>x>>y>>dist;
if (m.find(x)==m.end())
m[x] = m.size();
if (m.find(y)==m.end())
m[y] = m.size();
tx = m[x];
ty = m[y];
if (dist<g[tx][ty])
g[tx][ty] = g[ty][tx] = dist;
}
cin>>s;
cout<<"Total miles driven: "<<MST(m.size(), m["Park"], s)<<endl;
return 0;
}
【P.S】
相信自己,勇往直前!