hdu 4118 Holiday's Accommodation 2011 Asia ChengDu Regional Contest
原题连接:http://acm.hdu.edu.cn/showproblem.php?pid=4118
解题思路:
这个题的数据规模是N <= 10^5,这个是很大的,只有O(n)的算法才行,二分匹配之类的都不用想了,最后看到一个思路,就是每一个边,求其两头所连接的点个数的较小值乘以该边的权值乘以2,所有边求和就是所求的解,这个思路我想了一会儿就明白了。我的理解就是,对于每一个边,我们都可以将这个边两头的点交换到另一头,这样的话就是这些点都要走这个边,也就是说这个边要走的次数是2*Min(左头的点数,右边的点数)。对于每一条边这个都是可以实现的,对于整个图来说,也是可以实现的!
然后我就是递归写了一个搜索树的算法,但是,结果返回爆栈。我知道这个是由于递归的太深了,造成系统的栈无法承受,没办法,只好想怎么不用递归写了
这个题让我明白了一点,ACM不仅是考算法的,而且考谁聪明!
代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
int n;
int kk;
struct note
{
int v,next;
long long val;
}edge[200010];
int head[100010];
void add(int son,int fa,int value)
{
edge[kk].val = value;
edge[kk].v = son;
edge[kk].next = head[fa];
head[fa] = kk++;
}
long long re;
long long dp[100010];
bool vis[100010];
void _dfs(int root)
{
memset(vis,0,sizeof(vis));
stack <int> sta;
sta.push(root);
int t = head[root];
vis[root] = 1;
while(1)
{
int y;
if(t == -1)
{
y = sta.top();
sta.pop();
if(sta.empty()) break;
dp[sta.top()] += dp[y];
}
y = sta.top();
for(t = head[y];t != -1;t = edge[t].next)
{
int son = edge[t].v;
if(vis[son] == 1) continue;
vis[son] = 1;
sta.push(son);
break;
}
}
}
int main()
{
int t;
scanf("%d",&t);
for(int cas = 1;cas <= t;cas++)
{
memset(head,-1,sizeof(head));
re = 0;
kk = 1;
int x,y,z;
scanf("%d",&n);
for(int i = 1;i < n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for (int i=1;i<=n;i++) dp[i]=1;
_dfs(1);
for (int i=1;i<=2*(n-1);i+=2) {
x=edge[i].v;
y=edge[i+1].v;
long long d=min(dp[x],dp[y]);
re += min(d,n-d)*2*edge[i].val;
}
cout << "Case #" << cas << ": " << re << "\n";
}
return 0;
}