暑假训练DAY13测验(简单DP区间DP树型DP)

A - Tri Tiling

 POJ - 2663 

In how many ways can you tile a 3xn rectangle with 2x1 dominoes? 
Here is a sample tiling of a 3x12 rectangle. 

Input

Input consists of several test cases followed by a line containing -1. Each test case is a line containing an integer 0 <= n <= 30.

Output

For each test case, output one integer number giving the number of possible tilings.

Sample Input

2
8
12
-1

Sample Output

3
153
2131

我们可以发现每一个状态的数量都等于他的少2的情况×3加上少2的倍数的情况×2的和,也就是

p[2n]=3*p[2n-2]+2*(p[2n-4]+p[2n-6]+.........p[2]+p[0]);

需要注意的是0的时候状态数是1

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
//#define int long long
#define endl '\n'

using namespace std;

int main()
{
	int ans[31];
	memset(ans,0,sizeof(ans));
	ans[0]=1;
	ans[2]=3;
	for(int i=4;i<=30;i+=2)
	{
		ans[i]=4*ans[i-2]-ans[i-4];
	}
	int n;
	while(~scanf("%d",&n)&&n!=-1)
	{
		cout<<ans[n]<<endl;
	}
	return 0;
}

最大金额

时间限制: 1000MS 内存限制: 65536K
提交总数: 43758 接受: 13603

描述

给定一组n个整数:A = {a1,a2,...,an},我们定义函数d(A)如下:

你的任务是计算d(A)。

输入

输入由T(<= 30)个测试用例组成。测试用例(T)的数量在输入的第一行给出。 
每个测试用例包含两行。第一行是整数n(2 <= n <= 50000)。第二行包含n个整数:a1,a2,...,an。(| ai | <= 10000)。每个案例后都有一个空行。

产量

每个测试用例只打印一行。该行应包含整数d(A)。

Sample Input

1

10
1 -1 2 2 3 -3 4 -4 5 -5

Sample Output

13

暗示

在样本中,我们选择{2,2,3,-3,4}和{5},然后我们就可以得到答案。 

建议使用大量输入scanf。

正向和反向分别找出最大连续序列,枚举点找出正向和反向之和最大的点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
using namespace std;
const int size=5e5+5;
const long long inf=0x3f3f3f3f;
long long maxnl[size],maxnr[size];
long long arr[size];
int main()
{
	int i;
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		fill(maxnl,maxnl+n,0);
		fill(maxnr,maxnr+n,0);
		for(i=0;i<n;i++)
		{
			scanf("%lld",&arr[i]);
		}
		int ans=-inf,dp=0;
		for(i=0;i<n;i++)
		{
			if(dp>0)
			dp+=arr[i];
			else dp=arr[i];
			if(dp>ans) ans=dp;
			maxnl[i]=ans;
		}
		ans=-inf,dp=0;
		for(i=n-1;i>=0;i--)
		{
			if(dp>0)
			dp+=arr[i];
			else dp=arr[i];
			if(dp>ans) ans=dp;
			maxnr[i]=ans;
		}
		long long maxn=-inf;
		for(int k=0;k<n-1;k++)
		{
			//cout<<k<<' '<<maxnl[k]+maxnr[k]<<' '<<maxnl[k]<<endl;
			maxn=max(maxnl[k]+maxnr[k+1],maxn);
		}
		cout<<maxn<<endl;
	}
}

C - 不堪重负的树

 CSU - 1980 

小X非常喜欢树,然后他生成了一个大森林给自己玩。
玩着玩着,小X陷入了沉思。

  • 一棵树由N个节点组成,编号为i的节点有一个价值Wi。
  • 假设从树根出发前往第i个节点(可能是树根自己),一共需要经过Di个节点(包括起点和终点),那么这个节点对这棵树产生的负担就是Di与Wi的乘积。
  • 对于一棵树而言,这棵树的负担值为所有节点对它产生的负担之和。

小X学习了dfs,如果他知道树的结构,他当然可以很容易地算出树的负担值。可是现在沉思中的小X并不知道树的结构形态,他只知道一棵二叉树的中序遍历以及每个节点的价值,那么这棵二叉树可能的最小负担值是多少呢?

Input

第一行为一个正整数T(T≤20)表示数据组数。
每组数据包括三行。
第一行为一个正整数N(N≤200)。
第二行为N个正整数Wi(Wi≤108),表示编号为i的节点的价值。
第三行为N个正整数Pi(Pi≤N),为一个1~N的排列,表示二叉树的中序遍历结果。

Output

对于每组数据,输出一行一个正整数,表示这棵树可能的最小负担值。

Sample Input

2
4
1 2 3 4
1 2 3 4
7
1 1 1 1 1 1 1
4 2 3 5 7 1 6

Sample Output

18
17

Hint

对于第一个样例,树根为3,3的左儿子是2,3的右儿子是4,2的左儿子是1,这样构成的树可以达到最小负担。
对于第二个样例,对应的满二叉树可以达到最小负担。

典型的区间DP题,对各个区段的根节点进行遍历,找出其中的最小值,状态转移方程是

dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+sum[j]-sum[i-1]);(想不明白的话可以类比一下哈夫曼树的权值的计算)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#define int long long
#define endl '\n'
using namespace std;
int val[205];
int dp[205][205];
//int arr[205];
int sum[205];
const int inf= LLONG_MAX;

int32_t main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n;
		scanf("%lld",&n);
		memset(dp,0,sizeof(dp));
		//memset(arr,0,sizeof(arr));
		memset(val,0,sizeof(val));
		memset(sum,0,sizeof(sum));
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&val[i]);
			
			
		}
		for(int i=1;i<=n;i++)
		{
			int temp;
			scanf("%lld",&temp);
			sum[i]=sum[i-1]+val[temp];
			dp[i][i]=val[temp];
		}
		for(int l=2;l<=n;l++)
		{
			for(int i=1;i+l-1<=n;i++)
			{
				int j=i+l-1;
				dp[i][j]=inf;
				for(int k=i;k<=j;k++)
				{
					dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+sum[j]-sum[i-1]);
				}
			}
		}
		cout<<dp[1][n]<<endl;
	}
}

Balancing Act

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 16307 Accepted: 6895

Description

Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the largest tree in the forest T created by deleting that node from T. 
For example, consider the tree: 


Deleting node 4 yields two trees whose member nodes are {5} and {1,2,3,6,7}. The larger of these two trees has five nodes, thus the balance of node 4 is five. Deleting node 1 yields a forest of three trees of equal size: {2,6}, {3,7}, and {4,5}. Each of these trees has two nodes, so the balance of node 1 is two. 

For each input tree, calculate the node that has the minimum balance. If multiple nodes have equal balance, output the one with the lowest number. 

Input

The first line of input contains a single integer t (1 <= t <= 20), the number of test cases. The first line of each test case contains an integer N (1 <= N <= 20,000), the number of congruence. The next N-1 lines each contains two space-separated node numbers that are the endpoints of an edge in the tree. No edge will be listed twice, and all edges will be listed.

Output

For each test case, print a line containing two integers, the number of the node with minimum balance and the balance of that node.

Sample Input

1
7
2 6
1 2
1 4
4 5
3 7
3 1

Sample Output

1 2

求树的权重。先转化成有根树,然后从根节点进行遍历(找过的点打上标记不再搜索),找出每个点的子树的节点,取其中的最大值,而去完所有的子树之后剩余的剩余的点就是父节点方向的(可以想想,因为是从父节点来的,所以搜索时就不会搜索上那条边,同时父节点总是只有一个的,所以即使剩下的子树的根数)再取出其中的最小值即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define sc(x) scanf("%d",&x)
#define ii(x) int i=x
#define ms(name) memset(name,0,sizeof(name))
#define endl '\n'
const int inf=0x3f3f3f3f;
using namespace std;
vector<int> edge[20005];
#define vc(n) for(ii(1);i<=n;i++) edge[i].clear();
int dp[20005];
int flag[20005];
int nowmaxn,maxnpoint;
int n;
int dfs(int point)
{
	flag[point]=1;
	int maxn=0;
	for(ii(0);i<edge[point].size();i++)
	{
		if(!flag[edge[point][i]])
		{
			int tt=dfs(edge[point][i])+1;
			maxn=max(maxn,tt);
			dp[point]+=tt;
		}
	}
	int lost=n-1-dp[point];
	maxn=max(maxn,lost);
	if(maxn<nowmaxn)
	{
		maxnpoint=point;
		nowmaxn=maxn;
	} 
	
	return dp[point];
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ms(dp);
		ms(flag);
		
		sc(n);
		vc(n);
		for(ii(1);i<=n-1;i++)
		{
			int a,b;
			sc(a),sc(b);
			edge[a].push_back(b);
			edge[b].push_back(a);
		}
		nowmaxn=inf;
		
		dfs(1);
		cout<<maxnpoint<<' '<<nowmaxn<<endl;
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值