hdu 4616 Game

树形dp 

很容易想到的一种dp状态是dp[i][trap][fa]表示当前结点为i,经过trap个陷阱,从fa那里转移过来的最优解,虽然空间理论上只有O(n),但是要写成代码就得用vector或者map什么的。。。 明显的感觉要挂。。 (我赛后用map写600ms过了。。。数据弱么。。)多校的时候想到了这种状态。。但是没敢写T_T  4618那题也是这样 唉 太坑了T_T 以后不管怎样先写了再说T_T

附上map的代码。。。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <climits>
#include <ctime>
#include <numeric>
#include <vector>
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <complex>
#include <deque>
#include <functional>
#include <list>
#include <map>
#include <string>
#include <sstream>
#include <set>
#include <stack>
#include <queue>
using namespace std;
template<class T> inline T sqr(T x) { return x * x; }
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<PII, int> PIII;
typedef pair<LL, LL> PLL;
typedef pair<LL, int> PLI;
typedef pair<LD, LD> PDD;
#define MP make_pair
#define PB push_back
#define sz(x) ((int)(x).size())
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define istr stringstream
#define FOR(i,n) for(int i=0;i<(n);++i)
const double EPS = 1e-6;
const int INF = 0x3fffffff;
const LL LINF = INF * 1ll * INF;
const double PI = acos(-1.0);

using namespace std;

int n,c;
vector<int> g[50005];
int vv[50005],cc[50005];
map<int,int> dp[50005][4];

int dfs(int u,int tot,int fa){
    if(~fa&&dp[u][tot].find(fa)!=dp[u][tot].end()) return dp[u][tot][fa];
    int ans = vv[u];
    int tmp = tot+cc[u];
    if(tmp==c){
        if(~fa) return dp[u][tot][fa] = ans;
        else return ans;
    }
    int sz = g[u].size();
    for(int i = 0;i<sz;i++){
        int v = g[u][i];
        if(v==fa) continue;
        ans = max(ans,dfs(v,tmp,u)+vv[u]);
    }
    if(~fa) return dp[u][tot][fa] = ans;
    else return ans;
}

int main(void){
#ifndef ONLINE_JUDGE 
      freopen("/home/xing89qs/桌面/data.in","r",stdin);
#endif
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&c);
        for(int i = 0;i<n;i++){
            scanf("%d %d",&vv[i],&cc[i]);
            g[i].clear();
            for(int j = 0;j<=c;j++) dp[i][j].clear();
        }
        for(int i = 0;i<n-1;i++){
            int a,b;
            scanf("%d %d",&a,&b);
            g[a].PB(b);
            g[b].PB(a);
        }
        int ans = 0;
        for(int i = 0;i<n;i++)
            ans = max(ans,dfs(i,0,-1));
        printf("%d\n",ans);
    }
}



后来我再想想其实我可以按边来dp,因为其实dp[i][trap][fa]中i,fa这不就是表示的一个方向吗?  那么我们按边来dp考虑一下,dp[i][trap]表示沿着编号为i的边的方向,经过trap个陷阱的最优解,状态转移很显然的,看代码就知道了。。。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <climits>
#include <ctime>
#include <numeric>
#include <vector>
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <complex>
#include <deque>
#include <functional>
#include <list>
#include <map>
#include <string>
#include <sstream>
#include <set>
#include <stack>
#include <queue>
using namespace std;
template<class T> inline T sqr(T x) { return x * x; }
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<PII, int> PIII;
typedef pair<LL, LL> PLL;
typedef pair<LL, int> PLI;
typedef pair<LD, LD> PDD;
#define MP make_pair
#define PB push_back
#define sz(x) ((int)(x).size())
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define istr stringstream
#define FOR(i,n) for(int i=0;i<(n);++i)
const double EPS = 1e-6;
const int INF = 0x3fffffff;
const LL LINF = INF * 1ll * INF;
const double PI = acos(-1.0);

using namespace std;

int n,c;
int vv[50005],cc[50005];
int first[50005],next[100005];
struct Edge{
	int u,v;
} e[100005];
int dp[100005][4];

int cnt = 0;

void add(int a,int b){
	e[cnt] = (Edge){a,b};
	for(int i = 0;i<=c;i++) dp[cnt][i] = -1;
	int tmp = first[a];
	first[a] = cnt;
	next[cnt++] = tmp;
}

int dfs(int ed,int trap){
	if(trap==c) return 0;
	if(~dp[ed][trap]) return dp[ed][trap];
	int u = e[ed].u;
	int v = e[ed].v;
	int ans = vv[v];
	int tmp = trap+cc[v];
	for(int i = first[v];~i;i = next[i]){
		if(e[i].v==u) continue;
		ans = max(ans,vv[v]+dfs(i,tmp));
	}
	return dp[ed][trap] = ans;
}

int main(void){
#ifndef ONLINE_JUDGE 
      freopen("/home/xing89qs/桌面/data.in","r",stdin);
#endif
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d %d",&n,&c);
		for(int i = 0;i<n;i++){
			scanf("%d %d",&vv[i],&cc[i]);
			first[i] = -1;
		}
		cnt = 0;
		for(int i = 0;i<n-1;i++){
			int a,b;
			scanf("%d %d",&a,&b);
			add(a,b);
			add(b,a);
		}
		int ans = 0;
		for(int i = 0;i<cnt;i++){
			int u = e[i].u;
			ans = max(ans,vv[u]+dfs(i,cc[u]));
		}
		printf("%d\n",ans);
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值