Moving to Nuremberg - POJ 3847 dp

Moving to Nuremberg
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 610 Accepted: 176

Description

One of the most important inventions for modern-day city life is the public transportation. However, most people probably do not think of it that way { even though it makes travel in the city a lot easier, we generally want to spend as little time as possible on the subway. 
After your experience in NWERC 2009, Nuremberg holds a special place in your heart, and some years later you decide to move here. Your only problem is to figure out which part of Nuremberg to move to. Naturally, you want to move to a nice neighborhood, but since most parts of the city are nice there are still a lot of choices. Being naturally averse to spending hours each day on commuting, you instead decide to choose a place based on the amount of time you will have to spend on the subway. 
Now, if you were only going to go to one place, it would be easy to find the best place to live. But of course, there are several places where you anticipate that you will go regularly, such as work, friends, and the occasional Christkindlesmarkt. In order to be able to work this out, you have written a list of all the places which you want to visit regularly, along with estimates of how often you want to go there. For simplicity, you assume that you will always go somewhere and then back home, e.g., if you are going to Christkindlesmarkt after work you will drop by your house on the way from work before going to the markt, rather than going to the markt directly from work. Now, you have to find the place to live for which the total travel time is minimal. 
Because Nuremberg has an extensive public transportation system, you will be using the subway for traveling. The subway net is quite big, but is still fairly easily maneuvered because it is shaped like a tree. In other words, there is always a unique path between any pair of subway stations. (This is not quite true for the Nuremberg subway of today, but when you move here in a few years, we anticipate that it will be true.)

Input

The input consists of several test cases. The first line of input contains an integer c (1 <= c <= 200), giving the number of test cases. Then, each test case starts with an integer n (1 <= n <= 50 000), giving the number of subway stations in Nuremberg. Then follow n 1 lines, describing the subway net. Each of these lines contains three integers a, b, and t (1 <= a, b <= n, and 1 <= t <= 300), indicating that stations a and b are adjacent and that it takes t seconds to travel between them. This is followed by a line containing an integer m (0 <= m <= n), denoting the number of stations which you want to go to regularly. Then follow m lines. Each of these lines contains two integers a and f (1 <= a <= n, 1 <= f <= 500), where a is the station you want to visit and f is the number of times you want to visit this station in a year. No station will occur in this list more than once.

Output

For each test case, first output a line containing the number of seconds spent in traffic during a year, provided you choose an optimal place to live. Following this line, output a line giving all optimal choices of subway stations, separated by single spaces and in increasing order.

Sample Input

2
2
1 2 17
2
1 5
2 10
5
1 3 10
2 3 20
3 4 30
4 5 30
3
1 10
2 10
5 20

Sample Output

170
2
3000
3 4 5

题意:在一棵树中有n个节点,有些节点有需要访问的次数,你需要选择一个节点作为根节点,每次从根节点到一个节点再返回根节点算这个节点的一次访问,问最小的距离和是多少,并且哪些可以作为根节点。

思路:先以1为根节点的话dp[u][0]表示以u为根节点,它下面都访问过后的距离和,dp[u][1]表示根节点及其以下的所有访问数的和,设sum为所有的访问数的和。明显dp[u][0]+=dp[v][0]+dp[v][1]*dis(u,v);dp[u][1]+=dp[v][1];  然后再反过来假设一已知u的父节点为根节点的距离和,那么f[u]先去除u的子树这部分,dp2[f]-dp[u][0]-dp[u][1]*dis,再将其作为u的子节点带入上式,得dp2[f]-dp[u][0]-dp[u][1]*dis +dp[u][0] +(sum-dp[u][1])*dis;化简即可。

        另外继续吐槽这套题卡时间卡得太厉害了,不能用递归,和vector,要用数组模拟和用链表表示边。

AC代码如下:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAX 50010
using namespace std;
typedef long long ll;
struct node
{
    int v;
    ll dis;
    node *next;
}*head[MAX*2],tree[MAX*2];
node A;
ll dp[MAX][2],dp2[MAX],num[MAX],sum,minn;
int ans[MAX],qu[MAX],pre,pos,ptr,vis[MAX],f[MAX];
ll D[MAX];
void AddEdge(int u,int v,ll dis)
{
    tree[ptr].v=v;
    tree[ptr].dis=dis;
    tree[ptr].next=head[u];
    head[u]=&tree[ptr++];
}
void dfs1(int u)
{
    int i,j,k,v;
    dp[u][0]=0;dp[u][1]=num[u];
    node *p=head[u];
    while(p!=NULL)
    {
        v=p->v;
        if(v==f[u])
        {
            p=p->next;
            continue;
        }
        dp[u][0]+=dp[v][0]+dp[v][1]*D[v];
        dp[u][1]+=dp[v][1];
        p=p->next;

    }
}
void dfs2(int u)
{
    int i,j,k,v;
    //ll ret=dp2[f]-dp[u][0]-dp[u][1]*dis +dp[u][0] +(sum-dp[u][1])*dis;
    ll ret=dp2[f[u]]+(sum-dp[u][1]*2)*D[u];
    if(ret<minn)
    {
        minn=ret;
        ans[0]=1;
        ans[1]=u;
    }
    else if(ret==minn)
    {
        ans[0]++;
        ans[ans[0]]=u;
    }
    dp2[u]=ret;
}
int main()
{
    int T,t,n,m,i,j,k,u,v,len;
    ll dis;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
           head[i]=NULL;
        ptr=0;
        for(i=1;i<n;i++)
        {
            scanf("%d%d%I64d",&u,&v,&dis);
            AddEdge(u,v,dis);
            AddEdge(v,u,dis);
        }
        for(i=1;i<=n;i++)
           num[i]=0;
        scanf("%d",&m);
        sum=0;
        for(i=1;i<=m;i++)
        {
            scanf("%d",&u);
            scanf("%I64d",&num[u]);
            sum+=num[u];
        }
        pos=1;pre=0;
        qu[1]=1;f[1]=0;vis[1]=t;
        while(pre<pos)
        {
            pre++;
            u=qu[pre];
            node *p=head[u];
            while(p!=NULL)
            {
                v=p->v;
                if(vis[v]==t)
                {
                    p=p->next;
                    continue;
                }
                qu[++pos]=v;
                f[v]=u;D[v]=p->dis;
                vis[v]=t;
                p=p->next;
            }
        }
        for(i=n;i>=1;i--)
           dfs1(qu[i]);

        dp2[1]=dp[1][0];
        minn=dp[1][0];
        ans[0]=1;ans[1]=1;

        for(i=2;i<=n;i++)
           dfs2(qu[i]);

        printf("%I64d\n",minn*2);
        sort(ans+1,ans+1+ans[0]);
        printf("%d",ans[1]);
        for(i=2;i<=ans[0];i++)
           printf(" %d",ans[i]);
        printf("\n");
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值