您的位置:首页 > 其它

UVA 1218 完美的服务

2018-01-26 23:03 405 查看
https://vjudge.net/problem/UVA-1218

这题注意边界情况。在叶子节点d【u】【2】要注意区别取值,我这里采用的maxn标记。

而且d【u】【1】的取值,在u的孩子中如果有叶子节点那么这个策略是行不通的,也要标记。

接下来就是深度搜索了。我跟网上其他人的做法不太一样。这是一个比较易于理解的做法,写出来发现可以过就没再优化。其实计算d【u】【2】可以再快一点,具体看下面分析。

(u,0):u是服务器,则每个子结点可以是服务器也可以不是。

d(u,1):u不是服务器,但u的父亲是服务器,这意味着u的所有子结点都不是服务器。d(u,2):u和u的父亲都不是服务器。这意味着u恰好有一个儿子是服务器。

状态转移比前面复杂一些,但也不困难。首先可以写出:

d(u,0) = sum{min(d(v,0), d(v,1))} + 1

d(u,1) = sum(d(v,2))

而d(u,2)稍微复杂一点,需要枚举当服务器的子结点编号v,然后把其他所有子结

点v’的d(v’,2)加起来,再和d(v,0)相加。不过如果这样做,每次枚举v都需要O(k)时间(其中k是u的子结点数目),而v本身要枚举k次,因此计算d(u,2)需要花O(k 2 )时间。

刚才的做法有很多重复计算,其实可以利用已经算出的d(u,1)写出一个新的状态转移方程:

d(u,2) = min(d(u,1) – d(v,2) + d(v,0))

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<string.h>
#define maxn 10000+10
#define INF 100000000
vector<int> child[maxn];
int n;
int d[maxn][5];
int vis[maxn];
void initial()
{
for(int i=0;i<=n;i++)
child[i].clear();
memset(d,0,sizeof(d));
}

void dfs(int u)
{
int size=child[u].size();
if(!size)//leaf node
{
d[u][0]=1;
d[u][1]=0;
d[u][2]=maxn;//
return;
}

for(int i=0;i<size;i++)
{
int v=child[u][i];
dfs(v);

4000
d[u][0]+=min(d[v][0],d[v][1]);
}
d[u][0]++;

for(int i=0;i<size;i++)
{
int v=child[u][i];
if(!child[v].size())//leaf node
{
d[u][1]=maxn;break;
}
d[u][1]+=d[v][2];
}

int minn=INF;
for(int i=0;i<size;i++)
{
int v=child[u][i];
int sum=d[v][0];
for(int j=0;j<size;j++)
{
int v1=child[u][j];
if(j==i)continue;
sum+=(d[v1][2]>=maxn?0:d[v1][2]);
}
minn=min(minn,sum);
}
d[u][2]=minn;

return;
}

int main()
{
cin>>n;
while(1)
{
initial();
int n1,n2,root;
cin>>n1>>n2;
vis[n1]=vis[n2]=1;

root=n1;
child[n1].push_back(n2);
for(int i=1;i<=n-2;i++)
{
cin>>n1>>n2;

if(vis[n1])
{child[n1].push_back(n2);vis[n2]=1;}
else
{child[n2].push_back(n1);vis[n1]=1;}
}

dfs(root);

cin>>n;
int a=min(d[root][0],d[root][2]);
cout<<a<<endl;

if(n==-1)
break;
cin>>n;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: