树形DP——POJ3513

来源:互联网 发布:国家网络应急中心招聘 编辑:程序博客网 时间:2024/05/16 09:00
  • 题目链接: http://poj.org/problem?id=3513

  • 题意: 一家人去电影院,这一家人构成一个森林,每个森林都是一棵树。电影院有2种票:家庭票和个人票,买了家庭票,则自己的孩子可以不用买票(指下一代,不包括下下代..),买了个人票,则就是个人票。给出家人票和个人票的价格,求一种购票策略使得最终花费最小(花费相同,则求总票数最小)。最后输出个人票的数量,家庭票的数量和总花费。

  • 分析: 这题的读入有点麻烦,不过我们耐心搞定后其实就OK了,然后建好树后,我们发现这题就是一道树形DP的题,当前某棵子树的最优策略,其实就是这棵子树的根节点的花费+它的子树的最优策略。

  • 状态: 用数组 DP[i][0/1/2] 表示第 i 个节点的人不买票[0],买个人票[1],买家庭票[2]时的最少花费。

  • 转移方程:

DP[u][0] = MIN(DP[v][1], DP[v][2]); //儿子只能买个人票和家庭票DP[u][1] = S + MIN(DP[v][1], DP[v][2]); //儿子只能买个人票和家庭票DP[u][2] = F + MIN(DP[v][0], DP[v][2]);//儿子不买票或者买家庭票(买个人票毫无意义)
  • P.S. 之前我一直用的数组存储邻接链表建图,结果是一直T,换成用vector存储就过了,耗时还只有500ms,所以如果顶点数很多,是可以考虑用vector存图

  • AC代码:

/*************************************************************************    > File Name: test.cpp    > Author: Akira     > Mail: qaq.febr2.qaq@gmail.com  ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <algorithm>#include <bitset>#include <queue>#include <stack>#include <map>#include <cmath>#include <vector>#include <set>#include <list>#include <ctime>#include <climits>typedef long long LL;typedef unsigned long long ULL;typedef long double LD;#define MST(a,b) memset(a,b,sizeof(a))#define CLR(a) MST(a,0)#define Sqr(a) ((a)*(a))using namespace std;#define MaxN 100005#define MaxM MaxN#define INF 0x3f3f3f3f#define bug cout<<88888888<<endl;#define MIN(x,y) (x<y?x:y)#define MAX(x,y) (x>y?x:y)template<typename _> inline void scan(_& t){    int c;    while((c = getchar()) < '0' || c > '9');    t = c - '0';    while((c = getchar()) >= '0' && c <= '9') t = t * 10 + c - '0';}template<typename _> inline void print(_ x){    int len = 0, p[20];    if(x < 0) putchar('-'), x = -x;    while(x) p[++len] = x % 10, x /= 10;    if(!len) p[++len] = 0;    while(len) putchar(p[len--] + '0');}struct Node{    int s;    int f;    int cost;    int not_root;};vector<int> V[MaxN];int S,F;int a, b;int people;int notRoot[MaxN];char name[1005];map<string, int> M;map<string, int>::iterator it;Node DP[MaxN][3];void initNode(int &loc){    DP[loc][0].s =0;    DP[loc][0].f = 0;    DP[loc][0].cost = 0;    DP[loc][1].s =0;    DP[loc][1].f = 0;    DP[loc][1].cost = 0;    DP[loc][2].s=0;    DP[loc][2].f=0;    DP[loc][2].cost=0;    DP[loc][0].not_root = 0;    V[loc].clear();}int isNumber (string s){    int i, ans;    ans = 0;    for (i=0; i<s.length(); i++)    {        if ( s[i] >= '0' && s[i] <= '9' )                ans = ans*10 + (s[i]-'0');        else                return -1;    }    return ans;}void Initial (){    people = 0;    M.clear();}bool Input (){    //如果上个testcase已经将这次输入的第一个变量读入到a,就无需再读入    if ( a == -1 )  scanf("%d", &a);    scanf("%d", &b);    S = a;    F = b;    a = b = -1;    if ( S == 0 && F == 0 )            return false;    char ch;    int pid, sid;    string parent, son;    Initial();    while (1)    {        //bug;        //先将名字读入字符串数组,在将其置于string, 这样做是为了节省时间        scanf("%s", &name);                  parent = "";        parent.append(name);        a = isNumber(parent);        if ( a != -1 )  break;     //读入了下个testcase的数据        it = M.find(parent);     //查找有无对应映射        if ( it ==  M.end() )        {            M[parent] = people;            initNode(people);            people++;        }        pid = M[parent];        //读取当前节点的孩子        ch = getchar();        while ( ch != '\n' )        {            scanf("%s", &name);            son = "";            son.append(name);       //把字符数组的内容传给string            it = M.find(son);            if ( it == M.end() )            {                M[son] = people;                initNode(people);                people++;            }            sid = M[son];            V[pid].push_back(sid);            DP[sid][0].not_root = 1;            ch = getchar();        }    }   return true;}void cal(int &ans, int &s, int &f, Node a, Node b) //取最小花费和最小票数{    if(a.cost > b.cost)    {        ans += b.cost;        s += b.s;        f += b.f;    }    else if( a.cost < b.cost)    {        ans += a.cost;        s += a.s;        f += a.f;    }    else    {        int sa = a.s + a.f;        int sb = b.s + b.f;        ans += a.cost;        if(sa >= sb)        {            s+=b.s; f+=b.f;        }        else         {            s+=a.s; f+=a.f;        }    }}/*ans0DP[u][0] = MIN(DP[v][1], DP[v][2]);DP[u][1] = cost + MIN(DP[v][1], DP[v][2]);ans1DP[u][2] = cost + MIN(DP[v][0], DP[v][2]);*/void DFS(int u){    int ans0 = 0, ans1 = 0;    int ns0 = 0, ns1 = 0, nf0 = 0, nf1 = 0;    for(int i=0;i<V[u].size();i++)    {        int v = V[u][i];        DFS(v);        cal(ans0,ns0,nf0,DP[v][1],DP[v][2]);        cal(ans1,ns1,nf1,DP[v][0],DP[v][2]);    }    //不买票    DP[u][0].cost = ans0;    DP[u][0].s = ns0; DP[u][0].f = nf0;    //买个人票    DP[u][1].cost = S+ans0;    DP[u][1].s = ns0+1; DP[u][1].f = nf0;    //买家庭票    DP[u][2].cost = F+ans1;    DP[u][2].s = ns1; DP[u][2].f = (nf1+1);}void solve(){    int ans = 0, sn = 0, fn = 0;    for(int i=0;i<people;i++)    {        if(DP[i][0].not_root!=1)        {            DFS(i);            //cout << i <<"is not root" << endl;            cal(ans, sn, fn, DP[i][1], DP[i][2]);        }    }    printf("%d %d %d\n", sn, fn, ans);}int main(){    int cas = 1;    a = b = -1;    while(Input())    {        printf("%d. ", cas++);        solve();    }   // system("pause");}
0 0