树状动态规划

树的特点与性质:
1、 有n个点,n-1条边的无向图,任意两顶点间可达
2、 无向图中任意两个点间有且只有一条路
3、 一个点至多有一个前趋,但可以有多个后继
4、 无向图中没有环;

问题可以分解成若干相互联系的阶段,在每一个阶段都要做出决策,全部过程的决策是一个决策序列。要使整个活动的总体效果达到最优的问题,称为多阶段决策问题。动态规划就是解决多阶段决策最优化问题的一种思想方法。
阶段:将所给问题的过程,按时间或空间(树归中是空间,即层数)特征分解成若干相互联系的阶段,以便按次序去求每阶段的解。
状态:各阶段开始时的客观条件叫做状态。
决策:当各段的状态取定以后,就可以做出不同的决定,从而确定下一阶段的状态,这种决定称为决策。 (即孩子节点和父亲节点的关系)
策略:由开始到终点的全过程中,由每段决策组成的决策序列称为全过程策略,简称策略。
状态转移方程:前一阶段的终点就是后一阶段的起点,前一阶段的决策选择导出了后一阶段的状态,这种关系描述了由k阶段到k+1阶段(在树中是孩子节点和父亲节点)状态的演变规律,称为状态转移方程。
目标函数与最优化概念:目标函数是衡量多阶段决策过程优劣的准则。最优化概念是在一定条件下找到一个途径,经过按题目具体性质所确定的运算以后,使全过程的总效益达到最优。

道路和航路

描述

农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到T个城镇(标号为1..T),这些城镇通过R条标号为(1..R)的道路和P条标号为(1..P)的航路相连。

每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代价为Ci。每一条公路,Ci的范围为0<=Ci<=10,000;由于奇怪的运营策略,每一条航路的Ci可能为负的,也就是-10,000<=Ci<=10,000。

每一条公路都是双向的,正向和反向的花费是一样的,都是非负的。

每一条航路都根据输入的Ai和Bi进行从Ai->Bi的单向通行。实际上,如果现在有一条航路是从Ai到Bi的话,那么意味着肯定没有通行方案从Bi回到Ai。

农夫约翰想把他那优良的牛奶从配送中心送到各个城镇,当然希望代价越小越好,你可以帮助他嘛?配送中心位于城镇S中(1<=S<=T)。

输入

输入的第一行包含四个用空格隔开的整数T,R,P,S。

接下来R行,描述公路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

接下来P行,描述航路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

输出

输出T行,分别表示从城镇S到每个城市的最小花费,如果到不了的话输出NO PATH。


struct edge
{
	int v,len;
	edge(){
	}
	edge(int v1,int l)
	{
		v=v1,len=l;
	}
};
vector<edge> g[maxn];
void spfa()
{
	for(int i=0;i<=t;i++) dist[i]=inf,inque[src]=false;
	dist[src]=0;
	while(!q.empty()) q.pop_front();
	q.push_back(src);
	inque[src]=true;
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		for(int i=0;i<g[u].size();i++)
		{
			if(dist[u]+g[u][i].len<dist[g[u][i].v])
			{
				dist[g[u][i].v]=dist[u]+g[u][i].len;
				if(!inque[g[u][i].v])
				{
					if (dist[g[u][i].v] <= dist[q.front()]) q.push_front(g[u][i].v);
                    else q.push_back(g[u][i].v);
                    inque[g[u][i].v] = true;
				}
			}
		}
		inque[u]=false;
	}
}
int main()
{
	scanf("%d%d%d%d",&t,&r,&p,&src);
	for(int i=1;i<=r;i++)
	{
		int u,v,len;
		scanf("%d%d%d",&u,&v,&len);
		g[u].push_back(edge(v,len));
		g[v].push_back(edge(u,len));
	}
	for(int i=1;i<=p;i++)
	{
		int u,v,len;
        scanf("%d%d%d",&u,&v,&len);
		g[u].push_back(edge(v,len));
	}
	spfa();
	for(int i=1;i<=t;i++)
	{
		if(dist[i]<inf) printf("%d\n",dist[i]);
		else printf("NO PATH\n");
	}
}

产生数

问题描述

给出一个整数 n(n<10^30) 和 k 个变换规则(k<=15)。

规则:

一位数可变换成另一个一位数:

规则的右部不能为零。

例如:n=234。有规则(k=2):

2-> 5

3-> 6

上面的整数 234 经过变换后可能产生出的整数为(包括原数):

234

534

264

564

共 4 种不同的产生数

问题:

给出一个整数 n 和 k 个规则。

求出:

经过任意次的变换(0次或多次),能产生出多少个不同整数。

仅要求输出个数。

输入

n k

x1 y1

x2 y2

... ...

xn yn

输出

一个整数(满足条件的个数)

void mul(int x)
{
	int c = 0;//进位
	for (int i = 0; i <= p; i++) {//乘法
		num[i] = num[i] * x +c;
		c = num[i] / 10;
		num[i] = num[i] % 10;
	}

	while (c)//进位
	{
		num[++p] = c % 10;
		c /= 10;
	}
}

//使用dfs寻找可能的状态数
void dfs(int x)
{
	vis[x] = 1;
	for (int i = 0; i < a[x].size(); i++) {
		int v = a[x][i];
		if (!vis[v])dfs(v);
	}
}

int main()
{
	cin >> s >> n;
	for (int i = 0; i < n; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		a[x].push_back(y);
	}
	for (int i = 0; i < 10; i++) {
		memset(vis, 0, sizeof(vis));
		dfs(i);//找到每个数可能到达的状态数
		for (int j = 0; j < 10; j++) {
			if (vis[j])ha[i]++;//记录每个数能够到达的状态数
		}
	}

	num[0] = 1;
	p = 0;

	for (int i = 0; i < s.length(); i++) {
		mul(ha[s[i] - '0']);//接下来就是大数乘法,把每个数可能到达的数相乘
	}
	for (int i = p; i >= 0; i--)
	{
		printf("%d", num[i]);
	}
	printf("\n");
	return 0;
}

摆花

问题描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆。通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号。为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

Input

第一行包含两个正整数n和m,中间用一个空格隔开。

第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1、a2、……an。

Output

输出只有一行,一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对1000007

int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];	
		dp[0][0]=1;
	for(int i=1;i<=n;i++)
	for(int j=0;j<=m;j++)
	for(int k=0;k<=a[i];k++)
	{
		if(j>=k)
		dp[i][j]=(dp[i][j]+dp[i-1][j-k])%mod;
	}
	cout<<dp[n][m]<<endl;
    return 0;
}

危险系数

问题描述

抗日战争时期,冀中平原的地道战曾发挥重要作用。

地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

我们来定义一个危险系数DF(x,y):

对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。

本题的任务是:已知网络结构,求两站点之间的危险系数。

Input

输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;

接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;

最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。

Output

一个整数,如果询问的两点不连通则输出-1.


struct edge{
	int v,next;
}vec[maxn];
int head[maxn],mark[maxn],vis[maxn],way[maxn],tot,n,m,s,e,ans;
void dfs(int id,int x)
{
	way[id]=x;
	if(x==e)
	{
		ans++;
		for(int i=0;i<=id;i++)
			mark[way[i]]++;//标记路径中的点出现的次数
		return;
	}
	else
	{
		int v;
		for(int i=head[x];i!=-1;i=vec[i].next)
		{
			v=vec[i].v;
			if(!vis[v])
			{
				vis[v]=1;
				dfs(id+1,v);
				vis[v]=0;
			}
		}
	}
}
void add(int u,int v)
{
	vec[tot].v=v;
	vec[tot].next=head[u];
	head[u]=tot++;
}
int main()
{
	cin >> n >> m;
	int a,b;
	memset(head,-1,sizeof(head));
	for(int i=0;i<m;i++)
	{
		cin >> a >> b;
		add(a,b);
		add(b,a);
	}
	cin >> s >> e;
	vis[s]=1;
	dfs(0,s);
	int res=0;
	for(int i=1;i<=n;i++)
		if(mark[i]==ans)
			res++;
	cout << res-2;//结果-2,减去的是起点和终点
}

理财计划

问题描述

银行近期推出了一款新的理财计划“重复计息储蓄”。储户只需在每个月月初存入固定金额的现金,银行就会在每个月月底根据储户账户内的金额算出该月的利息并将利息存入用户账号。现在如果某人每月存入k元,请你帮他计算一下,n月后,他可以获得多少收益。

Input

输入数据仅一行,包括两个整数k(100<=k<=10000)、n(1<=n<=48)和一个小数p(0.001<=p<=0.01),分别表示每月存入的金额、存款时长、存款利息。

Output

输出数据仅一个数,表示可以得到的收益。

#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
    int k,n,t;//k存款金额,n存款时长
    double p,profits=0;
    cin>>k>>n>>p;
    t=k;
    for(int i=0;i<n;i++)
    {
        profits+=(profits+k)*p;
        k=k+t;
    }
    printf("%.2lf",profits);
}