-
A -- Tri Tiling
- Description
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
- 题目理解
很容易考虑出来当n为奇数的时候,不可能铺满所有的面积,所以种类为0;
这样的题目大部分是通过递推出来后面的结果,这时候我们思考当、、 、已经得出的时候来求.对于来说要变成水平方向增加两个单位,有三种铺法。当转变为要增加四个单位,这里如果是2+2模式已经包含在的可能中,所以应该在2|2分界线的地方放置瓷砖,由此可以增添两种可能,以此类推,之前更加小的都只带来两种可能因为2| ... |2在分界线需要放置瓷砖
得到转移方程
这里需要注意的是,不赋值会WA掉
#include<cstdio>
int main()
{
int a[32]={0},x;
a[0]=1;
a[2]=3;
a[4]=11;
for(int i=6;i<=30;i=i+2){
a[i]=4*a[i-2]-a[i-4];
}
while(scanf("%d",&x)!=EOF&&x!=-1){
printf("%d\n",a[x]);
}
return 0 ;
}
-
B -- Maximum sum
- Description
题目链接:http://poj.org/problem?id=2479
- Input
The input consists of T(<=30) test cases. The number of test cases (T) is given in the first line of the input.
Each test case contains two lines. The first line is an integer n(2<=n<=50000). The second line contains n integers: a1, a2, ..., an. (|ai| <= 10000).There is an empty line after each case
- Output
Print exactly one line for each test case. The line should contain the integer d(A)
- Sample Input
1
10
1 -1 2 2 3 -3 4 -4 5 -5
- Sample Output
13
- 题目理解
这道题需要有两段区间相加,可以不连续。定义为以结尾的大连续和。首先需要判断是否为负,如果为负则前面全不选只记录当前元素作为,前面使用一个变量记录前面最大的值然后选取最大值;如果为正,当前的值需要累加到值中再去与比较,这时候取前面的最大值;从后往前相同方法求前个元素的最大连续和。然后枚举中间断点保证两区间不相交。枚举得到最大值
#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;
const int maxn=50005;
int dp[maxn],num[maxn];
int main(){
int t,n,tmp,sum,ans;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
tmp=INT_MIN;
sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
sum+=num[i];
tmp=max(tmp,sum);
dp[i]=tmp;
if(sum<0) sum=0;//前面为负舍去前面的部分
}
tmp=ans=INT_MIN;
sum=0;
for(int i=n;i>1;i--)
{
sum+=num[i];
tmp=max(tmp,sum);
ans=max(ans,dp[i-1]+tmp);
if(sum<0) sum = 0;
}
printf("%d\n",ans);
}
return 0;
}
-
C -- 不堪重负的树
- Description
小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
- 题目理解
中序遍历所有结点的情况都不能确定但是我们知道根一定在中间,左区间为左子树,右区间为右子树;emmm很想区间,于是我么定义为结点组成的树最小负担,这时候很容易得到其中后面的连续和所增加的负担是因为,当两棵子树组成一棵树的时候所有结点相当于平行下移一个深度这时候加上根节点增加的负担就是所有结点负担之和
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=205;
ll a[maxn],sum[maxn],dp[maxn][maxn];
int main()
{
int t,n,leaf;
while(scanf("%d",&t)!=EOF){
while(t--){
memset(dp,0,sizeof(dp));
sum[0]=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for(int i=1;i<=n;++i){
scanf("%d",&leaf);
sum[i]=sum[i-1]+a[leaf];
dp[i][i]=a[leaf];//作为叶子需要初始化的值
}
for(int j=2;j<=n;++j){
for(int i=j-1;i>=1;--i){
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+sum[j]-sum[i-1];
for(int k=i+1;k<j;++k)
dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
printf("%lld\n",dp[1][n]);
}
}
return 0;
}
-
D -- Balancing Act
- 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<cstdio>
#include<vector>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
const int maxn=20005;
struct ver{
int v;
ver(int V):v(V){}
};
vector<ver> edge[maxn];
int dp[maxn],cnt[maxn],n;
void dfs(int father,int son){
int v;
for(int i=0;i<edge[son].size();++i){
v=edge[son][i].v;
if(v==father) continue;
dfs(son,v);
cnt[son]+=cnt[v];
}
dp[son]=n-1-cnt[son];
for(int i=0;i<edge[son].size();++i){
v=edge[son][i].v;
if(v==father) continue;
dp[son]=max(dp[son],cnt[v]);
}
cnt[son]++;
}
int main()
{
int t,u,v;
while(scanf("%d",&t)!=EOF){
while(t--){
for(int i=0;i<maxn;++i) edge[i].clear();
memset(cnt,0,sizeof(cnt));
scanf("%d",&n);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(0,1);
int ans=INT_MAX,pos=0;
for(int i=1;i<=n;++i){
if(dp[i]<ans){
ans=dp[i];
pos=i;
}
}
printf("%d %d\n",pos,ans);
}
}
return 0;
}
-
E -- Number Sequence
- Description
A single positive integer i is given. Write a program to find the digit located in the position i in the sequence of number groups S1S2...Sk. Each group Sk consists of a sequence of positive integer numbers ranging from 1 to k, written one after another.
For example, the first 80 digits of the sequence are as follows:
11212312341234512345612345671234567812345678912345678910123456789101112345678910
- Input
The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by one line for each test case. The line for a test case contains the single integer i (1 ≤ i ≤ 2147483647)
- Output
There should be one output line per test case containing the digit located in the position i
- Sample Input
2
8
3
- Sample Output
2
2
- 题目理解
和之前做过的一道递推题十分相似,首先通过规律记录增加的区间长度,通过与比较得出所在的。然后通过对分割成、、 ... 将数字段的最后一位的坐标以及数字段求出来,通过最后一位与相比较得出后面多余的位数,然后取模就可以输出
#include <cstdio>
#include <cmath>
const int maxn=31269;
long long num[maxn], sum[maxn];
void init()
{
num[1]=sum[1]=1;
for(int i=2;i<maxn;i++)
{
num[i] = num[i-1]+(int)log10((double)i)+1;
sum[i] = sum[i-1]+num[i];
}
}
int solve(int n)
{
int i=0,pos,cnt;
while(sum[i]<n) i++;
pos=n-sum[i-1];
cnt=0;
for(i=1;cnt<pos;i++)
cnt+=(int)log10((double)i)+1;
return (i-1)/(int)pow((double)10.0,(cnt-pos))%10;
}
int main()
{
int t, n;
init();
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
printf("%d\n", solve(n));
}
return 0;
}