树形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
- 树形DP——POJ3513
- poj2342—树形dp
- poj1463——树形DP
- poj4045——树形DP
- hdu3848——树形dp
- 树形——DP hdu 1520
- 树形DP——POJ1947 and POJ2486
- HDU 2196——Computer(树形DP)
- HDU2196——Computer(树形DP,经典)
- 【POJ2152】Fire——树形DP
- 树的重心——树形dp
- 树形DP——hiho 1055
- hzau1201——Friends(树形DP)
- 树形DP——Luogu2014 选课
- HDU1520 Anniversary party —— 树形DP
- HDU 2196 Computer——树形dp
- 树形dp小结——1
- 树形dp小结——2
- 新媒体文章标题怎么写?
- jenkins安装简要说明
- 二叉树的最大宽度和高度
- Hash碰撞
- 【BZOJ 1008】[HNOI2008]越狱
- 树形DP——POJ3513
- 删除当前用户IE临时文件win7版批处理
- Servlet初始化、运行、销毁全部过程
- 浅入浅出 Android 安全(五)Android 应用层安全
- java模拟栈的实现1
- 编程练习——字符串截取
- jqgrid插件在谷歌上不显示表格,不兼容
- 387. First Unique Character in a String
- Java--成员变量和局部变量,成员和静态变量区别