2014.2树形动规练习1

1、        加分二叉树

(binary.pas/c/cpp)

【问题描述】

    设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:

   subtree的左子树的加分×subtree的右子树的加分+subtree的根的分数

    若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。

    试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;

    (1)tree的最高加分

    (2)tree的前序遍历

 

【输入格式】

    第1行:一个整数n(n<30),为节点个数。

    第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。

 

【输出格式】

    第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。

    第2行:n个用空格隔开的整数,为该树的前序遍历。

 

【输入样例】

    5

    5 7 1 2 10

 

【输出样例】

   145

    31 2 4 5


【分析】用数组f[i,j]表示从节点i到节点j所组成的二叉树的最大加分,枚举根节点,则动态方程可以表示如下:

                f[i,j]=max{i<=t<=j |d[t]+f[i,t-1]*f[t+1,j]}

               初始: f(i,i)=d[i]

               目标:f(1,n)

               题目还要求输出最大加分树的前序遍历序列,因此必须在计算过程中记下从节点i到节点j所组成的最大加分二叉树的根节点,用数组b[i,j]表示

              也可以用记忆化搜索的方式,比循环不容易错

#include <cstdio>
int n,b[31][31],i,j,k;
long long f[31][31];


void pre(int l,int r)
{
   printf("%d ",b[l][r]);
   if (l<=b[l][r]-1)   pre(l,b[l][r]-1);
   if (b[l][r]+1<=r)   pre (b[l][r]+1,r);
}


int main()
{
   freopen("binary.in","r",stdin);  
   freopen("binary.out","w",stdout); 
   scanf("%d",&n);
   for (i=1;i<=n;i++)  
    { scanf("%I64d",&f[i][i]); 
      b[i][i]=i;
      f[i][i-1]=1; }
   f[n][n+1]=1;
   
   for (i=n;i>0;i--)
      for (j=i;j<=n;j++) //注意循环顺序,保证从底向上  
       if (!f[i][j])
       {         
          for (k=i;k<=j;k++)         
              if (f[i][k-1]*f[k+1][j]+f[k][k]>f[i][j])
                 {
				    f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
                    b[i][j]=k;
				 }
	   }
	printf("%I64d\n",f[1][n]);
	pre(1,n);
	printf("\n");
//	getchar(); getchar();
}

2、 Ural 1018 二叉苹果树

(apple.pas/c/cpp)

数据有问题,请提交网络

提交地址:http://acm.timus.ru/problem.aspx?space=1&num=1018

题目

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
  

2   5
    \ /
     3   4
      \ /
       1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。

输入格式

第1行2个数,N和Q(1<=Q<=N,1<N<=100)。
N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。

输出格式

一个数,最多能留住的苹果的数量。

样例输入

5 2
1 3 1
1 4 10
2 3 20
3 5 20

样例输出

21

#include<cstdio>
#include<cstring>
using namespace std;

struct Node{
    int l,r,s;
	}tree[110];
int app[110][110],n,q;
bool v[110];
long f[110][110];

void make_tree(int root)
{
  v[root]=false;
  for (int i=1;i<=n;i++)
    if (v[i] && app[root][i]!=-1){
	   if (tree[root].l==0) tree[root].l=i;
	   else tree[root].r=i;
	   tree[i].s=app[root][i];
	   make_tree(i);
	   }
}
	
void init()
{
   freopen("apple.in","r",stdin);  
   freopen("apple.out","w",stdout);
   int i,a,b,x,j;
   scanf("%d%d",&n,&q);
   for (i=0;i<=n+1;i++) for (j=0;j<=n+1;j++) app[i][j]=-1;
   for (i=1;i<n;i++){
	 scanf("%d%d%d",&a,&b,&x);
	 app[a][b]=app[b][a]=x;
	 }
   memset(v,1,sizeof(v));
   make_tree(1);
   memset(f,0,sizeof(f));
   for (i=1;i<=n;i++) f[i][1]=tree[i].s;
 }
 
 long rfs(int root,int k)
{
  if (f[root][k]) return f[root][k];
  if (root==0 ||k==0) return f[root][k]=0;
  for (int i=0;i<=k-1;i++)
   {
      long ls=rfs(tree[root].l,i);
      long rs=rfs(tree[root].r,k-i-1);
      if (ls+rs>f[root][k])	f[root][k]=ls+rs;
    }
   return f[root][k]+=tree[root].s;
}   
   

int main()
{
  init();
  printf("%ld\n",rfs(1,q+1));
 // getchar(); getchar();
  }

3、选课

(class.pas/c/cpp)

[问题描述]

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b)。一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?

输入:

第一行有两个整数N,M用空格隔开。(1<=N<=300,1<=M<=200)

接下来的N行,第I+1行包含两个整数ki和si, ki表示第I门课的直接先修课,si表示第I门课的学分。若ki=0表示没有直接先修课(1<=ki<=N,1<=si<=20)。

输出:

只有一行,选M门课程的最大得分。

样例:

输入:class.in

7  4

2  2

0  1

0  4

2  1

7  1

7  6

2  2

输出:class.out

13

【思路】 森林转化为左二子右兄弟;依赖性背包

 

#include<cstdio>
#include<cstring>

int n,m,s[310],brother[1000],child[1000],son[1000],f[1000][1000];

void set_tree()
{
   int i,k;
   scanf("%d%d",&n,&m);
   for (int i=1;i<=n;i++)
   {
     scanf("%d%d",&k,&s[i]);
     if (son[k]==0) child[k]=i;
      else brother[son[k]]=i;
     son[k]=i;
    }
	memset(f,0,sizeof(f));
}
          

int rfs(int root,int k)
{
    if (k==0|| root==0) return f[root][k]=0; //root=0
	if (f[root][k]>0) return f[root][k];
//	if (k==1) return f[root][k]=s[root]; 
	int max=-1;
	for (int i=0;i<k;i++)
	   {
	      int s1=rfs(child[root],i);
		  int s2=rfs(brother[root],k-1-i);
		  if (s1+s2>max) max=s1+s2;
		  }
	max+=s[root];
	int t=rfs(brother[root],k);
	if (max>t) return f[root][k]=max;
	  else return f[root][k]=t;
	  }
int main()
{
  freopen("class.in","r",stdin);  
  freopen("class.out","w",stdout);
  set_tree();
  printf("%d\n",rfs(child[0],m));
  //getchar(); getchar();
  }
  

4、计算机网络(network)

Ural 1056

http://acm.timus.ru/

计算机网络

[问题描述]

计算机网络是不断地把新的计算机连接到已经在网络中的计算机上形成的。每个新的计算机都有一个序号,而协议包含的是它的父结点的序号。因此,协议包含了一些数;第一个总是1,因为第二台计算机只能连接在第一台上,第二个数字是1或2,等等。协议一共包括N-1个数(N是计算机的数量)。

例如,协议1,1,2,2代表如下的网络           

1---2---5

|   |

3  4

计算机之间的距离是指链上的连接的数量。因此,在上面这个例子中,#4和#5之间的距离是2,#3和#5之间的距离是3。

定义:一个计算机网络的中心是指到最远的计算机距离最近的那个计算机。在上面的这个例子里#1和#2都是计算机网络的中心。

你的任务是找到这个协议下的中心。N为整数。1<N<=10000

[问题输入]

第一行是一个整数N,计算机的数量。下面是这个协议包含的N-1个数字。

[问题输出]

顺序输出这个网络的中心。

[样例输入]

5

1

1

2

2

[样例输出]

1 2

 

//http://acm.timus.ru/1056
#include<cstdio>
#include<cstring>
#include<climits>

int point[10001],next[10001],last[10001],fa[10001];
int down[10000][3];//0 max 1 smax
int up[10000],n;

void init()
{
  // freopen("network.in","r",stdin);
  // freopen("network.out","w",stdout);
   scanf("%d",&n);
   int x,tot=0;
   for (int i=2;i<=n;i++){
    scanf("%d",&x);
    fa[i]=x;
    point[i-1]=i;
    next[i-1]=last[x];
    last[x]=i-1;
	}
}
	
void fdown(int x)
{
   int p=last[x];
   while (p)
   {
      int i=point[p];
      fdown(i);
      if (down[i][1]+1>down[x][1]) 
      {
         down[x][2]=down[x][1];
         down[x][1]=down[i][1]+1;
         }
         else 
           if (down[i][1]+1>down[x][2]) down[x][2]=down[i][1]+1;
      p=next[p];
      }
}                           
	
	  
	
void fup(int x)
{
  if (x==1) up[x]=0;
  else {
       int t;
       up[x]=up[fa[x]]+1;
	   if (down[fa[x]][1]==down[x][1]+1)
	      t=down[fa[x]][2]+1;  else  t=down[fa[x]][1]+1;
		if (t>up[x]) up[x]=t;
		}
  int p=last[x];
  while (p)
  {
    fup(point[p]);
    p=next[p];
  }
}
	

void getans()
{
     int dis,mindis,centre1,centre2;
     mindis=INT_MAX;
     for (int i=1;i<=n;i++)
       {
         dis=up[i]>down[i][1]?up[i]:down[i][1];
         if (dis<mindis)
           {
             centre1=i;
             mindis=dis;}
          if (dis==mindis)
             centre2=i;   
         }
      printf("%d",centre1);   
      if (centre2!=centre1) printf(" %d\n",centre2);
}
                    

int main()
{
   init();
   fdown(1);
   fup(1);
   getans();
  // getchar(); getchar();
   }
   


5、Tju1053 技能树

(skill.pas/c/cpp)

Problem

玩过Diablo的人对技能树一定是很熟悉的。一颗技能树的每个结点都是一项技能,要学会这项技能则需要耗费一定的技能点数。

只有学会了某一项技能以后,才能继续学习它的后继技能。每项技能又有着不同的级别,级别越高效果越好,而技能的升级也是需要耗费技能点数的。

有个玩家积攒了一定的技能点数,他想尽可能地利用这些技能点数来达到最好的效果。因此他给所有的级别都打上了分,他认为效果越好的分数也越高。现在他要你帮忙寻找一个分配技能点数的方案,使得分数总和最高。

Input

该题有多组测试数据。

每组测试数据第一行是一个整数n(1<=n<=20),表示所有不同技能的总数。

接下来依次给出n个不同技能的详细情况。

每个技能描述包括5行。

第一行是该技能的名称。

第2行是该技能在技能树中父技能的名称,名称为None则表示该技能不需要任何的先修技能便能学习。

第3行是一个整数L(1<=L<=20),表示这项技能所能拥有的最高级别。

第4行共有L个整数,其中第I个整数表示从地I-1级升到第I级所需要的技能点数(0级表示没有学习过)。

第5行包括L个整数,其中第I个整数表示从第I-1级升级到第I级的效果评分,分数不超过20。

在技能描述之后,共有两行,第1行是一个整数P,表示目前所拥有的技能点数。

接下来1行是N个整数,依次表示角色当前习得的技能级别,0表示还未学习。这里不会出现非法情况。

Output

每组测试数据只需输出最佳分配方案所得的分数总和。

Sample Input

3
Freezing Arrow
Ice Arrow
3 
3 3 3 
15 4 6
Ice Arrow
Cold Arrow
2
4 3
10 17
Cold Arrow
None
3
3 3 2
15 5 2
10
0 0 1

Sample Output

42

 

#include<string>
#include<cstdio>
using namespace std;

int n,p,i,l[50],brother[50],child[50],son[50];
long f[50][1000];

struct Node{
       char n[100],f[100];
       int l,cost[50],value[50],learn;
       }sk[50];
  
       
int  find(char ch[100])
{
   for (int j=1;j<=n;j++)
     if (strcmp(sk[j].n,ch)==0) return j;
   return 0;
}
     
void clear()
{     
   
   memset(son,0,sizeof(son));
   memset(brother,0,sizeof(brother));
   memset(child,0,sizeof(child));
   memset(f,-1,sizeof(f));
   memset(sk,0,sizeof(sk));
}

void set_tree()
{
   int father;  
   clear();
   for (i=1;i<=n;i++)
     {
        scanf("\n");             
        scanf("%100[^\n]",&sk[i].n);
        scanf("\n");
        scanf("%100[^\n]",&sk[i].f);
        scanf("%d",&sk[i].l);
        for (int j=1;j<=sk[i].l;j++) scanf("%d",&sk[i].cost[j]);
        for (int j=1;j<=sk[i].l;j++) scanf("%d",&sk[i].value[j]); 
        }
     scanf("%d",&p);
     for (i=1;i<=n;i++) scanf("%d",&sk[i].learn);
     for (i=0;i<=p;i++) f[0][i]=0;
     for (i=1;i<=n;i++) 
     {
         father=find(sk[i].f);
        if (son[father]==0) child[father]=i;
            else brother[son[father]]=i;
        son[father]=i;     
     }
}  

long rfs(int root,int j)
{
    if (root==0) return f[root][j]=0; // cost may=0,so when j=0 could continue
    if (f[root][j]>=0) return f[root][j];
    long max,co=0,va=0,s;
    max=rfs(brother[root],j);
    if (sk[root].learn) //
      for (int k=1;k<=j;k++)
       {
           s=rfs(child[root],k)+rfs(brother[root],j-k);
           if (s>max) max=s;
           }
    for (int g=sk[root].learn+1;g<=sk[root].l;g++)
      {
         co+=sk[root].cost[g];
         va+=sk[root].value[g];
         for (int k=0;k<=j-co;k++)
          {       
             s=rfs(child[root],k)+rfs(brother[root],j-co-k)+va;
             if (s>max) max=s;
             }
         }
    return f[root][j]=max;
}       
             
int main()
{
   freopen("skill.out","w",stdout);
   freopen("skill.in","r",stdin); 
   while (scanf("%d",&n)!=EOF)
   {   
       set_tree();
       printf("%ld\n",rfs(child[0],p));
       }
}   


6、战略游戏

(strategi.pas/c/cpp)

Problem

Bob喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。

他要建立一个古城堡,城堡中的路形成一棵树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵.

Input

第一行为一整数M,表示有M组测试数据

每组测试数据表示一棵树,描述如下:

第一行 N,表示树中结点的数目。

第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面有k条边与结点I相连)。

接下来k个数,分别是每条边的另一个结点标号r1,r2,...,rk。

对于一个n(0<n<=1500)个结点的树,结点标号在0到n-1之间,在输入数据中每条边只出现一次。

Output

输出文件仅包含一个数,为所求的最少的士兵数目。

例如,对于如下图所示的树:

答案为1(只要一个士兵在结点1上)。

Sample Input

2
4
0 1 1
1 2 2 3
2 0
3 0
5
3 3 1 4 2
1 1 0
2 0
0 0
4 0

Sample Output

1

【】注意与下一题的区别

//点覆盖边 
#include<cstdio>
#include<cstring>
using namespace std;

int m,n,root,tot=0;
int f[1501][2];
int point[1501],next[1501],last[1501];
int mark[1501];

int  min(int a,int b) { if (a<b) return a; else return b;  }

void add(int a,int b)
{
   point[++tot]=b;
   next[tot]=last[a];
   last[a]=tot;
}  

void dp(int u)
{
   f[u][1]=1;
   f[u][0]=0;
   int p=last[u];
   while (p)
   {
        int v=point[p];
        dp(v); 
		f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][1],f[v][0]);
		p=next[p];		
   }		
}
          		  

int main()
{
    freopen("strategi.in","r",stdin);
    freopen("strategi.out","w",stdout);
    //scanf("%d",&m);
	int x,k,y;
//	while (m)
	//{
	  // m--;
	   scanf("%d",&n);
	   memset(mark,0,sizeof(mark));
	   //memset(point,0,sizeof(point));
	 //  memset(last,0,sizeof(last));
	  // memset(next,0,sizeof(next));
	    for (int i=0;i<n;i++)
	    {
		    scanf("%d%d",&x,&k);
			for (int j=1;j<=k;j++)
			{
			   scanf("%d",&y);
			  add(x,y);
			  mark[y]=1;
			  }
			 }
	    for (int i=0;i<n;i++) if (mark[i]==0) { root=i; break; }
		dp(root);
		printf("%d\n",min(f[root][0],f[root][1]));
	}	
}	
		


7、皇宫看守(程序文件名:Guard.pas)

问题描述:太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
  皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状;某些宫殿间可以互相望见。大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
  可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
编程任务:帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
数据输入:输入数据由文件名为INPUT.TXT的文本文件提供。输入文件中数据表示一棵树,描述如下:
  第1行 n,表示树中结点的数目。
  第2行至第n+1行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号i(0<i<=n),在该宫殿安置侍卫所需的经费k,该边的儿子数m,接下来m个数,分别是这个节点的m个儿子的标号r1,r2,...,rm。
  对于一个n(0 < n <= 1500)个结点的树,结点标号在1到n之间,且标号不重复。
数据输出:输出到OUTPUT.TXT文件中。输出文件仅包含一个数,为所求的最少的经费。
                   如左图的输入数据示例      输出数据示例
    6                    25

1 30 3 2 3 4

2 16 2 5 6

3 5 0

4 4 0

5 11 0

6 50          

        

 

//点覆盖点 
#include<cstdio>
#include<climits>

int n,mark[1501]={0},cost[1501],tot,root,num[1501],son[1501][1501];
long f[1501][3];

//void add(int a,int b) {point[++tot]=b;	next[tot]=last[a];	last[a]=tot; }
long min(long a,long b) { if (a<b) return a; else return b; }

/*void dp(int u)  打错的= =
{
    f[u][0]=0;
    f[u][1]=
    f[u][1]=cost[u];
    int p=last[u];
    while (p)
   {	
       int v=point[p];
	   dp(v);
	   f[u][0]+=f[v][1];
	   f[u][1]+=min(f[v][1],f[v][0]);
	   p=next[p];
	   }
   printf("%d %ld %ld\n",u,f[u][0],f[u][1]);
}*/

void dp(int u)
{
     if (num[u]==0) 
     {
       f[u][0]=f[u][2]=cost[u];
       f[u][1]=0;
       return;
       }
     for (int k=1;k<=num[u];k++) dp(son[u][k]);
     f[u][0]=LONG_MAX; 
     f[u][1]=0;
     f[u][2]=cost[u];
     for (int k=1;k<=num[u];k++)
     {
         long tmp=0;
         int uson=son[u][k];
         for (int i=1;i<=num[u];i++)
           if (son[u][i]!=uson) tmp+=min(f[son[u][i]][0],f[son[u][i]][2]);
         f[u][0]=min(f[u][0],tmp+f[uson][2]);
         f[u][1]+=min(f[uson][0],f[uson][2]);
         f[u][2]+=min(min(f[uson][0],f[uson][1]),f[uson][2]);   
            }      
                                     
}                   

int main()
{
    freopen("guard.in","r",stdin);
    freopen("guard.out","w",stdout);
    scanf("%d",&n);
	int x,m,r;
	for (int i=1;i<=n;i++)
	  {
	     scanf("%d",&x);
         scanf("%d%d",&cost[x],&num[x]);
		 for (int j=1;j<=num[x];j++) 
		 {
		    scanf("%d",&son[x][j]);
			mark[son[x][j]]=1;
			}
		}
	for (int i=1;i<=n;i++) if (mark[i]==0) { root=i; break; }
	if (n==1) printf("%d\n",cost[n]); //
	else {
	      dp(root);
          printf("%ld\n",min(f[root][0],f[root][2]));
          }
    //getchar(); getchar();
}	

 

8、聚会的快乐(party.pas)

  你要组织一个由你公司的人参加的聚会。你希望聚会非常愉快,尽可能多地找些有趣的热闹。但是劝你不要同时邀请某个人和他的上司,因为这可能带来争吵。给定N个人(姓名,他幽默的系数,以及他上司的名字),编程找到能使幽默系数和最大的若干个人。

  输入:

  第一行一个整数N(N<100)。接下来有N行,每一行描述一个人的信息,信息之间用空格隔开。姓名是长度不超过20的字符串,幽默系数是在0到200之间的整数。

  输出:

  所邀请的人最大的幽默系数和。

  样例:

  party.in

  5

  BART 1 HOMER

  HOMER 2 MONTGOMERY

  MONTGOMERY1 NOBODY

  LISA 3 HOMER

  SMITHERS 4 MONTGOMERY

  Party.out

  8


#include<cstdio>
#include<cstring>

int n,tot,point[101],next[101],last[101],root;
int f[101][2];
int mark[101]={0};
 
struct Person{
     char n[21],f[21];
	 int va;
	 }a[101];
	 
int find(char c[21])
{
     for (int j=1;j<=n;j++) 
	   if (strcmp(a[j].n,c)==0) return j;
	 return 0;
	 }
	 
void add(int a,int b)
{
     point[++tot]=b;
     next[tot]=last[a];
     last[a]=tot;
     }	 
     
int max(int a,int b) { if (a>b) return a; else return b; }	
	
void dp(int u)
{
 
      f[u][0]=0;
      f[u][1]=a[u].va;
      
   int p=last[u];
   while (p)
   {
      int v=point[p];
      dp(v);
      f[u][0]+=max(f[v][0],f[v][1]);
      f[u][1]+=f[v][0];
      p=next[p];
      }
}
                            
     	 
int main()
{
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout);
    scanf("%d",&n);
	for (int i=1;i<=n;i++)
	  scanf("%s%d%s",&a[i].n,&a[i].va,&a[i].f);
	for (int i=1;i<=n;i++)
	  {
	      int father=find(a[i].f);
		  add(father,i);
		  mark[i]=1;
          }
    for (int i=1;i<=n;i++) if (mark[i]==0) { root=i; break; }
    dp(root);
    printf("%d\n",max(f[root][0],f[root][1]));
}
    
    



9、信号放大器

源程序名            booster.???(pas, c, cpp)

可执行文件名        booster.exe

输入文件名          booster.in

输出文件名          booster.out

【问题描述】

       树型网络是最节省材料的网络。所谓树型网络,是指一个无环的连通网络,网络中任意两个结点间有且仅有一条通信道路。

       网络中有一个结点是服务器,负责将信号直接或间接地发送到各终端机。如图6-4,server结点发出一个信号给结点a和c,a再转发给b。如此,整个网络都收到这个信号了。

server            a                  b

●────○────○

       但是,实际操作中,信号从一个结点发到另一个结点,会出现信号强度的衰减。衰减量一般由线路长度决定。

                                           server    3    a      2         b

●────○────○

│1

       如上图,边上所标的数字为边的衰减量。假设从server出发一个强度为4个单位的信号,发到结点a后强度衰减为4-3=1个单位。结点a再将其转发给结点b。由于信号强度为1,衰减量为2,因此信号无法发送到b。

       一个解决这一问题的方法是,安装信号放大器。信号放大器的作用是将强度大于零的信号还原成初始强度(从服务器出发时的强度)。

       上图中,若在结点a处安装一个信号放大器,则强度为4的信号发到a处,即被放大至4。这样,信号就可以被发送的网络中的任意一个节点了。为了简化问题,我们假定每个结点只处理一次信号,当它第二次收到某个信号时,就忽略此信号。

       你的任务是根据给出的树型网络,计算出最少需要安装的信号放大器数量。

【输入】

       第一行一个整数n,表示网络中结点的数量。(n<=100000)

       第2~n+1行,每行描述一个节点的连接关系。其中第i+1行,描述的是结点i的连接关系:首先一个整数k,表示与结点i相连的结点的数量。此后2k个数,每两个描述一个与结点i相连的结点,分别表示结点的编号(编号在1~n之间)和该结点与结点i之间的边的信号衰减量。结点1表示服务器。

       最后一行,一个整数,表示从服务器上出发信号的强度。

【输出】

       一个整数,表示要使信号能够传遍整个网络,需要安装的最少的信号放大器数量。

       如果不论如何安装信号放大器,都无法使信号传遍整个网络,则输出“Nosolution.”

【样例】

       booster.in                                   booster.out

       4                                        1

       2 2 3 3 1

       2 1 3 4 2

       1 1 1

       1 2 2

       4


【思路】贪心,详见》》 http://www.docin.com/p-601990756.html

#include<cstdio>
#define maxn 1000
int i,j,n,server,set,tot,x,y;

struct Te{
      int son[maxn],del[maxn];
	  }tree[10001];
	  
bool checknoanswer()
{
     for (i=1;i<=n;i++) 
	   for (j=1;j<=tree[i].son[0];j++) 
	     if (tree[i].del[j]>=server) return 0;
	return 1;
	}

void MAXX(int &a,int b) { if (b>a) a=b; }	
int dp(int root)
{
    if (tree[root].son[0]==0) return 1;
	int max=0;
	for (int j=1;j<=tree[root].son[0];j++) 
	 {
        int tmp=dp(tree[root].son[j]);
        if (tmp+tree[root].del[j]>server) { set++;  tmp=1; }  
	    MAXX(max,tmp+tree[root].del[j]);
     }
	//if (max>server) { set++; return (max-server); }
	return max;
	}
	
int main()
{
     freopen("booster.in","r",stdin);
     freopen("booster.out","w",stdout);
     scanf("%d",&n);
	 for (i=1;i<=n;i++){
	     scanf("%ld",&tree[i].son[0]);
         tot=0;
         for (j=1;j<=tree[i].son[0];j++)  {
              scanf("%d%d",&x,&y);
              if (x>i){
                 tree[i].son[++tot]=x;
                 tree[i].del[tot]=y;
                 }
           }
          tree[i].son[0]=tot;
     }
	 scanf("%d",&server);
	 if (!checknoanswer()) {
		    printf("No solution.\n");
			return 0;
			}
	set=0;
	dp(1);
	printf("%d\n",set);
//	getchar(); getchar();
}
	


10、“访问”艺术馆

源程序名       gallery.???(pas, c, cpp)   可执行文件名   gallery.exe

输入文件名     gallery.in                 输出文件名    gallery.out

【问题描述】

       经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

【输入】

       第1行是警察赶到的时间,以s为单位。第2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第2个数是它末端的藏画数量;如果第2个数是0,那么说明这条走廊分叉为两条另外的走廊。数据按照深度优先的次序给出,请看样例。

       一个展室最多有20幅画。通过每个走廊的时间不超过20s。艺术馆最多有100个展室。警察赶到的时间在10min以内。

【输出】

       输出偷到的画的数量。

【样例】

       gallery.in(如图6-6)                       gallery.out

       60                                                     2

       70 8 0 3 1 14 2 10 0 12 4 6 2

 

#include<cstdio>

int stime,a,b;
int stack[110],top,tot;
int f[101][601];

struct Node{
    int l,r,pic,t;
	}g[101];

void init(int u) //建图 
{
    if (scanf("%d",&a)!=EOF)  scanf("%d",&b); else return;
	g[u].t=a*2;
	if (b==0) 
	{ 
	   g[u].l=++tot;
	   g[u].r=++tot;
	   init(g[u].l);
	   init(g[u].r);
	}
	   else 
	    {
	        g[u].pic=b;
			return;
		}
} 		

int  dp(int root,int time )
{
   if (root==0 ||time==0)  return f[root][time]=0;
   if (f[root][time]>0) return f[root][time];
   if (g[root].l==0 && g[root].r==0)
      return f[root][time]=(time-g[root].t)/5<g[root].pic?(time-g[root].t)/5:g[root].pic;       
   for (int k=0;k<=time-g[root].t;k++)
    {
       int s1=dp(g[root].l,k);
       int s2=dp(g[root].r,time-g[root].t-k);
       if (s1+s2>f[root][time]) f[root][time]=s1+s2;
       }
   return f[root][time];
}
	
int main()
{
    freopen("gallery.in","r",stdin);
    freopen("gallery.out","w",stdout);
    scanf("%d",&stime);
    stime--;
    tot=1;
	init(1);
	printf("%d\n",dp(1,stime));
	getchar(); getchar(); 
	}
		    




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值