ブルーブリッジカップ-2015年第6回C/C ++Zhenti[州大会][グループB]

コンテンツ

1.宝くじの枚数(空欄に記入)

2.ギャラクシーボム(結果を記入)

3. Sanyang Xianrui(空欄に記入)

4.グリッドに出力します(空白を埋めるためのコード)

5. 9つのグループのスコア(空欄に記入)

6.乗算への加算(結果を入力)

7.カードの種類の数(空欄に記入)

8.移動距離(プログラミングの質問)

9.サイコロを振る(プログラミングの質問)

10.生命の木(プログラミングの質問)

11.まとめ


1.宝くじの枚数(空欄に記入)

「4」の数字など、数字に迷信を抱き、不運と思われる「死」のホモフォニーだと考える人もいます。
これらの主張はまったくナンセンスですが、時には一般の人々のニーズにも応えます。ある宝くじの抽選番号は5桁(10000〜99999)です。「4」の数字は入れないでください。主催者から、発行できる宝くじの最大数を計算してもらいます。 2つの宝くじクーポンは複製されません。
この番号(整数)を、余分なテキストや説明文なしで送信してください。

アイデア:この質問は非常に単純です。ここで2つの方法を提供します。最初の方法は、範囲内の数値の余りを直接取得することです。4に等しい桁に等しい場合は、スキップします。等しくない場合は、 4、次に++。内部の数字を文字列に変換し、findを使用して各数字に4が含まれているかどうかを確認します。

方法1:

#include<iostream>
using namespace std;
int cnt = 0;
bool f(int x)
{
	while (x > 0)
	{
		int m = x % 10;
		if (m == 4)
		{
			return false;
		}
		x /= 10;
	}
	return true;
}
int main()
{
	for (int i = 10000; i <= 99999; i++)
	{
		if (f(i))
		{
			cnt++;
		}
	}
	cout << cnt << endl;
	return 0;
}

方法2:

#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int ants;
void i2s(int i, string& s)
{
	stringstream ss;
	ss << i;
	ss >> s;
}
int main()
{
	for (int i = 10000; i <= 99999; i++)
	{
		string s;
		i2s(i,s);
		if (s.find('4') == string::npos)
		{
			ants++;
		}
	}
	cout << ants << endl;
	return 0;
}

回答:52488 

2.ギャラクシーボム(結果を記入)

広大な銀河Xに浮かぶ、宇宙の道路標識として使われているXスターの人工「爆弾」がたくさんあります。
各爆弾は、設定された日数後に爆発するように設定できます。
例:アルファ爆弾が2015年1月1日に配置され、タイミングが15日だった場合、2015年1月16日に爆発します。
2014年11月9日に1000日のタイミングで配置されたベータ爆弾があります、それが爆発した正確な日付を計算してください。

日付はyyyy-mm-ddの形式で入力してください。つまり、年は4桁、月は2桁、日付は2桁です。例:2015-02-19
フォーマットに厳密に従って書いてください。他の単語や記号は表示されません。

 アイデア:これは空欄の質問なので、コンピューターに付属しているExcelでそれを行うことができ、メニューバーに電卓が接続されています。

方法1:

方法2:コードの実装

#include<iostream>
using namespace std;
class Date {
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int GetMonthDay(int year, int month)
	{
		static int monthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int day = monthDayArray[month];
		// 365天 5小时+
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			day += 1;
		}

		return day;
	}
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			++_month;

			if (_month == 13)
			{
				_month = 1;
				_year++;
			}
		}

		return *this;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2014, 11, 9);
	d1 += 1000;
	d1.print();
	return 0;
}

回答:2017-8-5 

3. Sanyang Xianrui(空欄に記入)

次の追加アルゴリズムを確認してください。

    Xiangruishenghui
 + Sanyang Xianrui
 ------------------- Sanyang
Shengqiang

(位置合わせに問題がある場合は、[図1.jpg]を参照してください)

その中で、同じ漢字は同じ数字を表し、異なる漢字は異なる数字を表します。

「SanyangXianrui」で表される4桁の数字(答えは一意)を入力してください。冗長な内容は入力しないでください。

アイデア:この質問は、8つのforループで激しく列挙できます。とにかく、効率の問題を考慮せずに、空白を埋める質問です。STLライブラリのnext_permutationを使用して完全に配置し、判断することもできます。質問の意味を満たしているかどうか。最後の方法は、数学的なアイデアを組み合わせてforループを減らし、アルゴリズムの効率を向上させることです。

三->a->a [0]

羊->b->a [1]

して->c->a [2]

瑞->d->a [3]

祥->e->a [4]

生->f->a [5]

辉->g->a [6]

気->h->a [7]

方法1:暴力的な列挙、8つの漢字を8つの異なる文字に置き換えるああ

#include<iostream>
#include<bits/stdc++.h>
int main(){
	int sum, sum1, sum2;
	for(int a=1;a<=9;a++){ //a 不能等于 0 
		for(int b=0;b<=9;b++){
			if(b!=a){
				for(int c=0;c<=9;c++){
					if(c!=a&&c!=b){
						for(int d=0;d<=9;d++){
							if(d!=a&&d!=b&&d!=c){
								for(int e=1;e<=9;e++){ //e 不能等于 0 
									if(e!=a&&e!=b&&e!=c&&e!=d){
										for(int f=0;f<=9;f++){
											if(f!=a&&f!=b&&f!=c&&f!=d&&f!=e){
												for(int g=0;g<=9;g++){
													if(g!=a&&g!=b&&g!=c&&g!=d&&g!=e&&g!=f){
														for(int h=0;h<=9;h++){
															if(h!=a&&h!=b&&h!=c&&h!=d&&h!=e&&h!=f&&h!=g){
																sum1=e*1000+d*100+f*10+g;
																sum2=a*1000+b*100+c*10+d;
																sum=a*10000+b*1000+f*100+d*10+h;
																if(sum==(sum1+sum2)){
																	printf("%d\n",sum2);
																}
															}
															
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return 0;
}

方法2:STLライブラリのnext_permutationを使用してすべてを配置し、配列aを使用して文字を置き換えます

//  a[4] a[3] a[5] a[6]
//+ a[0] a[1] a[2] a[3]
//------------------------------ -
//a[0] a[1] a[5] a[3] a[7]
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{

	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int sum1 = 0;
	int sum2 = 0;
	int sum = 0;
	while (next_permutation(a, a + 10))
	{
		if (a[0] != 0 && a[4] != 0)
		{
			sum1 = a[4] * 1000 + a[3] * 100 + a[5] * 10 + a[6];
			sum2 = a[0] * 1000 + a[1] * 100 + a[2] * 10 + a[3];
			sum = a[0] * 10000 + a[1] * 1000 + a[5] * 100 + a[3] * 10 + a[7];
			if ((sum1 + sum2) == sum)
			{
				break;
			}
		}
	}
	cout << sum2 << endl;
	return 0;
}

回答:1085 

4.グリッドに出力します(空白を埋めるためのコード)

StringInGrid関数は、指定された文字列を指定されたサイズのグリッドに出力します。
ストリングは、水平方向と垂直方向の両方の中央に配置する必要があります。
文字列が長すぎる場合、切り捨てられます。
正確に中央に配置できない場合は、少し左または少し上にすることができます。
次のプログラムはこのロジックを実装しています。下線部分に不足しているコードを入力してください。

#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
 int i,k;
 char buf[1000];
 strcpy(buf, s);
 if(strlen(s)>width-2) buf[width-2]=0;
 
 printf("+");
 for(i=0;i<width-2;i++) printf("-");
 printf("+\n");
 
 for(k=1; k<(height-1)/2;k++){
  printf("|");
  for(i=0;i<width-2;i++) printf(" ");
  printf("|\n");
 }
 
 printf("|");
 
 printf("%*s%s%*s",_____________________________________________);  //填空
          
 printf("|\n");
 
 for(k=(height-1)/2+1; k<height-1; k++){
  printf("|");
  for(i=0;i<width-2;i++) printf(" ");
  printf("|\n");
 } 
 
 printf("+");
 for(i=0;i<width-2;i++) printf("-");
 printf("+\n"); 
}
int main()
{
 StringInGrid(20,6,"abcd1234");
 return 0;
}

 タイトルのデータについては、次のように出力されます。
+ -------- +
| |
| abcd1234 |
| |
| |
+ ------------ ------ +
アイデア:この種のコードの穴埋め質問を行うには、まず、入力する場所をコメント化し、次にそれを実行して効果を確認します。

つまり、文字の位置を特定してから、入力する必要のあるコードセグメントを確認する必要があります。

printf("%*s%s%*s",_____________________________________________);  

 * sこのテストは比較的偏っていますが、cplusplusをチェックすることはできません。

 実際、これはプレースホルダーと同等です

ここで、つまり、幅幅から文字列の長さを引いたものから2を引いたものと、全体を2で割って答えを得ることがわかります。

回答案:(width-2-strlen(buf))/ 2、 ""、buf、(width-2-strlen(buf))/ 2、 "") 

5. 9つのグループのスコア(空欄に記入)

1,2,3 ... 9これらの9つの数値は分数を形成し、その値は正確に1/3です。どのように作成しますか?
次のプログラムはこの機能を実装しています。下線部分に不足しているコードを入力してください。

#include <stdio.h>
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];

if(a*3==b) printf("%d / %d\n", a, b);
}

void f(int x[], int k)
{
int i,t;
if(k>=9){
test(x);
return;
}

for(i=k; i<9; i++){
{t=x[k]; x[k]=x[i]; x[i]=t;}
f(x,k+1);
_____________________________________________ // 填空处
}
}

int main()
{
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);  
return 0;
}

 アイデア:実際、私の以前のブログを読んだ人なら誰でも、これが完全な配列テンプレートであり、再帰的なバックトラックであることを知っています。答えを直接入力できます。

回答案:t = x [k]; x [k] = x [i]; x [i] = t;

6.乗算への加算(結果を入力)

1 + 2 +3+…+49= 1225
では、隣接していない2つのプラス記号を乗算記号に変換する必要があり、結果は2015年になります。

例:
1 + 2 +3+…+10* 11 +12+…+27* 28 +29+…+49= 2015
は、要件を満たす答えです。

別の考えられる答えを探して、最初の乗算記号の左側にある番号を送信してください(たとえば、10を送信してください)。

 アイデア:この質問は実際には列挙型です。2つの乗算記号の位置を列挙する必要があります。最初の乗算記号の位置は、1から46の位置までで、隣接しておらず、2番目の乗算記号があることを確認します。乗算記号。2番目の乗算記号の位置は最初の位置から離す必要があるため、開始位置は最初の乗算記号の位置+ 2で、最後の演算子である48までです。2つの式は次のとおりです。乗算記号の位置を除いて同じであり、2015に等しい式を1225に等しい式から差し引くことができます。

コード:

#include<iostream>
using namespace std;
int main()
{
	for(int i=1;i<=46;i++)
		for (int j = i + 2; j <= 48; j++)
		{
			if (i * (i + 1) - (i + i + 1) + j * (j + 1) - (j + j + 1) == 2015 - 1225)
			{
				cout << i << " " << j << endl;
			}
	 }
	return 0;
}

回答:16

7.カードの種類の数(空欄に記入)

Xiao MingはXカジノに乗っ取られ、他の3人とトランプをすることを余儀なくされました。トランプのデッキ(大小の切り札を除いて、合計52枚)が4人に均等に分配され、それぞれ13枚のカードがあります。このとき、シャオ・ミンの頭に突然疑問が浮かびました。スーツを考慮せず、ポイントとカードの順番だけを考えれば、最初のカードの組み合わせはいくつ手に入れることができますか?

余計な内容や説明文を入れずに整数を記入してください。

 アイデア:質問を分析することで、この質問が実際には順列と組み合わせの問題であることがわかります。順列と組み合わせの解決策のすべての可能性を試す必要があります。この機能によると、再帰を使用して問題を解決できます(再帰を検索できます)が、この質問には制限があります。ポイントの数のみを考慮し、カードの順序は考慮しないため、解くときに、完全な配置を解くなど、さまざまな並べ替え方法のさまざまな解として計算することはできません。 。実際、この問題は、各番号の出現回数を解決するために変換できます。カードが異なる順序でカウントされるという問題は避けてください。問題に対する考え方が異なると、問題の処理が容易になる場合があります。

コード:

#include <iostream>
#define ll long long
using namespace std;
ll ans = 0;
void dfs(int cnt, int num) {
	//拿到十三张牌
	if (cnt == 13) ans++;
	//牌的数量超过十三张
	if (cnt >= 13) return;
	//拿到第十四种牌 (当然是不允许的)
	if (num > 13) return;
	//遍历第num种牌的拿取方式 
	for (int i = 0; i <= 4; i++) {
		dfs(cnt + i, num + 1);
	}
}
int main() {

	dfs(0, 1);
	cout << ans << endl;
	return 0;
}

回答:3598180

8.移動距離(プログラミングの質問)

惑星Xの住宅地域の建物はすべて同じで、マトリックスパターンで配置されています。その建物の数は1、2、3 ...です。
列がいっぱいになると、次の列の隣接する建物とは反対の方向に番号が配置されます。
例:セルの行番号の幅が6の場合、開始状況は次のようになります。

1 2 3 4 5 6
12 11 10 9 8 7
131415…

私たちの問題は次のとおりです。2つの建物番号mとnを知っているので、それらの間の最短移動距離を見つける必要があります(斜めに移動することはできません)

入力は、スペースで区切られた3つの整数wmnで、すべて1〜10000の範囲です
。wは行番号の幅、m、nは計算される建物番号です。
mnの2つのフロア間の最短移動距離を表す整数を出力する必要があります。

例:
ユーザー入力:
6 8 2
次に、プログラムは次を出力する必要があります:
4

別の例:
ユーザー入力:
4 7 20
、プログラムは出力する必要があります:
5

リソースの規則:
ピークメモリ消費量<256MCPU
消費量<1000ms

要件に厳密に従って出力し、「入力してください...」のような余分なコンテンツを表面的に印刷しないでください。

すべてのコードは同じソースファイルに配置され、デバッグ後、ソースコードをコピーして送信します。

注:main関数は0を返す必要があります。
注:ANSI C / ANSI C ++標準のみを使用し、コンパイル環境またはオペレーティングシステムに依存する特別な関数を呼び出さないでください。
注:すべての依存関数はソースファイルに明示的にインクルードする必要があり、プロジェクト設定で共通のヘッダーファイルを省略することはできません。

送信するときは、目的のコンパイラタイプを選択するように注意してください。

アイデア:この質問は、いくつかの数式をテストすることです。最初に、2つの数値mとnが配置されている行を計算します。嚢を計算する方法は?これはm%w、n%wであり、0に等しいかどうかを判断します。0に等しい場合、行数はrm = m / w、rn = n / wに等しくなります(0に等しくない場合)。 、次にrm = m / w + 1、rn = n / w + 1; 2つ目は、列の数を決定することです。列は反対方向に配置されているため、最初に偶数列か奇数列かを決定します。 rm%2偶数列にある場合は、rm * w-m + 1自体です。奇数列にない場合は、原因までいくつかの数値を入力しますw-(rm * wm);rnについても同じことが言えます。 。

#include<iostream>
using namespace std;
int main()
{
	int w, m, n;
	cin >> w >> m >> n;
	int rm = m % w == 0 ? m / w : m / w + 1;
	int rn = n % w == 0 ? n / w : n / w + 1;
	int cn = 0, cm = 0;
	if (rm % 2 == 0)cm = rm * w - m + 1;
	else
		cm = w - (rm * w - m);
	if (rn % 2 == 0) cn = rn * w - n + 1;
	else
		cn = w - (rn * w - n);
	cout << abs(rm - rn) + abs(cm - cn) << endl;
	return 0;
}

9.サイコロを振る(プログラミングの質問)

ギャンブルの聖人ATMは、晩年にサイコロに恋をしました。つまり、サイコロは積み重ねられています。
長期間の観察の後、atmは安定したサイコロの謎を発見しました:いくつかの数字は互いに反発します!
最初にダイスを標準化しましょう。1の反対は4、2の反対は5、3の反対は6です。
相互に排他的な現象のグループがm個あるとすると、各グループの2つの数字の面は互いに近く、サイコロを安定して積み重ねることはできません。
atmは、サイコロを振る方法がいくつあるかを数えたいと考えています。
サイコロを置く2つの方法は、両方の方法で対応する高さのサイコロの対応する数が同じに面している場合にのみ同じです。
解の数が多すぎる可能性があるため、10 ^ 9+7を法として結果を出力してください。

ATMのサイコロの数を過小評価しないでください〜

「入力形式」
2つの整数の最初の行nmn
はサイコロの数を表し、
次のm行、2つの整数abの各行は、aとbの数値を密接に関連付けることができないことを示します。

「出力形式」は
、1行に1つの数値であり、10 ^ 9+7を法とする答えの結果を示します。

「サンプル入力」211
2

「サンプル出力」
544

「データ範囲」データ
の30%の場合:n <= 5
データの60%の場合:n <= 100
データの100%の場合:0 <n <= 10 ^ 9、m <= 36

リソースの規則:
ピークメモリ消費
量<256MCPU消費量<2000ms

要件に厳密に従って出力し、「入力してください...」のような余分なコンテンツを表面的に印刷しないでください。

すべてのコードは同じソースファイルに配置され、デバッグ後、ソースコードをコピーして送信します。

注:main関数は0を返す必要があります。
注:ANSI C / ANSI C ++標準のみを使用し、コンパイル環境またはオペレーティングシステムに依存する特別な関数を呼び出さないでください。
注:すべての依存関数はソースファイルに明示的にインクルードする必要があり、プロジェクト設定で共通のヘッダーファイルを省略することはできません。

送信するときは、目的のコンパイラタイプを選択するように注意してください。 

アイデア:再帰と動的計画法を使用してこの問題を実行できますが、部分的なポイントしか取得できません。重要なことは、これら2つのアイデアを理解することです。マトリックスを使用して、この問題を後で解決します。

方法1:再帰

//递归
#include<iostream>
using namespace std;
#define mod 1000000007
int op[7];//记录对立面
bool  conflict[7][7];//判断是否冲突
int n, m;
void Init()
{
	op[1] = 4;
	op[2] = 5;
	op[3] = 6;
	op[4] = 1;
	op[5] = 2;
	op[6] = 3;
}
long long int  f(int up, int cnt)
{
	if (cnt == 0)
	{
		return 4;
	}
	long long ants = 0;
	for (int upp = 1; upp <= 6; upp++)
	{
		if (conflict[op[up]][upp])continue;
		ants = (ants + f(upp, cnt - 1)) % mod;
	}
	return ants;
}
int main()
{
	Init();
	cin >> n >> m;
	int x, y;
	long long ants = 0;
	for (int i = 0; i < m; i++)
	{
		cin >> x >> y;
		conflict[x][y] = true;
		conflict[y][x] = true;
	}
	for (int up = 1; up <= 6; up++)
	{
		ants = (ants + 4*f(up, n - 1))%mod;
	}
	cout << ants << endl;
	return 0;
}

方法2:動的計画法

#include<iostream>
#include<map>
using namespace std;
long long dp[2][7];//dp[i][j]表示有i层,限定朝上的数字为j的稳定方案数
#define mod 1000000007
//int op[7];//记录对立面
bool  conflict[7][7];//判断是否冲突
map<int, int>op;
int n, m;
void Init()
{
	op[1] = 4;
	op[2] = 5;
	op[3] = 6;
	op[4] = 1;
	op[5] = 2;
	op[6] = 3;
}
int main()
{
	Init();
	cin >> n >> m;
	int x, y;
	long long ants = 0;
	for (int i = 0; i < m; i++)
	{
		cin >> x >> y;
		conflict[x][y] = true;
		conflict[y][x] = true;
	}
	//输入完成
	for (int j = 1; j <= 6; j++)
	{
		dp[0][j] = 1;
	}
	int cur = 0;
	//迭代层数
	for (int level = 2; level <= n; level++)
	{
		cur = 1 - cur;
		//尝试把6个面放在当前一层朝上的方向
		for (int j = 1; j <= 6; j++)
		{
			dp[cur][j] = 0;
			//将与op[j]不冲突的上一层格子里面的数累加起来
			for (int i = 1; i <= 6; i++)
			{
				if (conflict[op[j]][i])continue;//冲突的面朝上是不可取的
					dp[cur][j] = (dp[cur][j] + dp[1 - cur][i]) % mod;
			}
		}
	}
	long long sum = 0;
		for (int k = 1; k <= 6; k++)
		{
			sum = (sum + dp[cur][k] )% mod;
		}
	//快速冥求4的次方
	long long ans = 1;
	long long tmp = 4;
	long long p = n;
	while (p != 0)
	{
		if (p & 1 == 1) ans = (ans * tmp) % mod;
		tmp = (tmp * tmp) % mod;
		p >>= 1;
	}
	cout << (sum * ans) % mod << endl;
			return 0;
}

 方法3:マトリックス

#include<bits/stdc++.h>
#define ag(x) ((x)>3?(x)-3:(x)+3)
using namespace std;
typedef long long ll;
ll mod=1e9+7; 
struct matrix{
	int n,m;
	ll s[10][10];
};
//对A的初始化修改成 初始化为4,因为骰子四个面可以互相转动,需要最终乘以4或者初始化为4
matrix Aunit(matrix A){
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			A.s[i][j]=4;
		}
	}
	return A;
}
//返回一个单位矩阵
matrix unit(matrix A){
	matrix re;
	re.n=A.n;re.m=A.m;
	for(int i=0;i<re.n;i++){
		for(int j=0;j<re.m;j++){
			if(i==j)re.s[i][j]=1;
			else re.s[i][j]=0;
		}
	}
	return re;
}
//两个矩阵相乘
matrix mix(matrix A,matrix B){
	matrix re;re.n=A.n;re.m=B.m;
	for(int i=0;i<re.n;i++){
		for(int j=0;j<re.m;j++){
			re.s[i][j]=0;
			for(int k=0;k<A.m;k++){
			    re.s[i][j]+=A.s[i][k]*B.s[k][j]%mod;
			    re.s[i][j]%=mod;
			}
		}
	}
	return re;
}
//快速求 矩阵A的b次方
matrix dpow(matrix A,ll b){
    matrix re;
	re=unit(A);
	while(b){
		if(b&1)re=mix(re,A);
		A=mix(A,A);
		b>>=1;
	}
	return re;
}
int main(){
	ll n,m;
    scanf("%lld%lld",&n,&m);
    matrix A;A.n=6;A.m=6;
	A=Aunit(A); //初始化矩阵A,表示冲突矩阵
	int ip1,ip2;
	//设置冲突矩阵的冲突面(两两对应)
	while(m--){
		scanf("%d%d",&ip1,&ip2);
		A.s[ip2-1][ag(ip1)-1]=0;
		A.s[ip1-1][ag(ip2)-1]=0;
	}
	matrix p;p.n=1;p.m=6;
	for(int j=0;j<6;j++)p.s[0][j]=4;
	A=dpow(A,n-1); //求A矩阵的n-1次方
    p=mix(p,A); //最后算A^n-1矩阵 * p矩阵,(p就是高度为1的第一个矩阵对应dp[1])
    ll ans=0;
    for(int j=0;j<6;j++)ans=(ans+p.s[0][j])%mod;
	printf("%lld\n",ans);
	return 0;
}

10.生命の木(プログラミングの質問)

Xの森で、神は生命の木を創造されました。彼は、各ツリーの各ノード(リーフはノードとも呼ばれます)に、このポイントの調和値を表す整数でマークを付けました。神はこのツリーで空でないノードセットSを選択したいので、Sの任意の2つのポイントa、bに対して、それぞれが次のようなポイントリスト{a、v1、v2、...、vk、b}があります。このポイントリストのポイントはSの要素であり、シーケンス内の2つの隣接するポイントの間にエッジがあります。 
この前提の下で、神はSの点に対応する整数の合計をできるだけ大きくしたいと望んでおられます。この最大の合計は、生命の木に対する神のスコアです。  
atmの努力の後、彼は神がすべての木のすべてのノードに与えた整数をすでに知っていました。しかし、ATMは計算が苦手なので、効率的にスコアを取得する方法がわかりません。彼はあなたが木のスコアを計算するためのプログラムを書く必要があります。   
「入力形式」 の最初の行の整数nは
、ツリーにn個のノードがあることを示します。2行目には、各ノードのスコアを表すn個の整数が含まれています。 
次のn-1行では、各行に2つの整数u、vが含まれており、uからvへのエッジがあることを示しています。これは木なので、サイクルはありません。   
「出力フォーマット」 
は、神が木に与えたスコアを示す数字の行を出力します。   
「サンプル入力」 


1 -2 -3 4 5

4 2

3 1

1 2

25   
「サンプル出力」

 8   
「データ範囲」データ 
の30%の場合、n <= 10 
データの100%の場合、0 <n <= 10 ^ 5の場合、各ノードのスコアの絶対値は10^6を超えません。   
リソースの規則: 
ピークメモリ消費量<256MCPU消費量<3000ms

アイデア:ノードごとに2種類の決定があり、選択するかしないかです。次に、それぞれdp[i][0]とdp[i][1]を定義して、iノードを選択しない(選択する)ことを示します。の最大重み合計を取得できます。状態遷移方程式は次のとおりです。dp[i][1]= sum(max(dp [j] [1]、dp [j] [0]));jはiの子です;dp [i] [0] = 0;質問で与えられたタイトルは根なしツリーであるため、DFSを実行するときにマークする必要があります。検索対象のノードの子ノードにアクセスした場合、それはリーフノードです。また、再帰的な境界にあります(このブランチの検索を停止します)。

#include<cstdio>
#include<cstring>
#include<vector>
#define N 100005
using namespace std;
vector<int> node[N];
// dp[i][0],dp[i][1];
// 分别表示选i结点和不选能得到的最大分数 
int dp[N][2];
int v[N],vis[N];
int n,a,b;
void dfs(int u){
	dp[u][1] = v[u];
	dp[u][0] = 0;
	vis[u]=1;
	for(int i=0 ;i<node[u].size();i++){
		if(!vis[node[u][i]]){
			dfs(node[u][i]);
	
			dp[u][1] += max(dp[node[u][i]][1],dp[node[u][i]][0]);
		
		}else{
			dp[u][1] = max(dp[u][1],v[u]);
			dp[u][0] = max(dp[u][0],0);
		}
	}
} 
 
void init(){
	memset(v,0,sizeof(v));
	memset(dp,0,sizeof(dp));
	scanf("%d",&n);
	for(int i=1 ;i<=n ;i++){
		scanf("%d",&v[i]);
	}
	for(int i=1 ;i<n ;i++){
		scanf("%d%d",&a,&b);
		node[a].push_back(b);
		node[b].push_back(a);
	}
}
 
int main(){
	init();
	
	dfs(1);
	int ans = -1;
	for(int i=1 ;i<=n ;i++){
//		printf("dp[%d][1]:%d\n",i,dp[i][1]);
//		printf("dp[%d][0]:%d\n",i,dp[i][0]);
		ans = max(ans,dp[i][1]);
		ans = max(ans,dp[i][0]);
	}
	printf("%d\n",ans);
	return 0;
} 

11.まとめ

1.宝くじの数の  列挙         +文字列検索

2.銀河爆弾              の簡単な計算

3. Sanyang Xianrui               列挙+判断、未知数を減らすための数学的推論

4.グリッドでの%* sの出力には、           幅と出力コンテンツの2つのパラメーターが必要です。

5.9つのグループの分数           の再帰的な全順列

6.乗算列挙への追加   、巧妙な計算        

7.カードタイプの数の               再帰

8.距離              を移動して、例で記号式を見つけます

9.ベースダイス                 マトリックス操作

10.生命の木の無限ツリーには、           ルートの数であるDFSがあり、各ノードをルートとして使用したときに取得できる最大の重みと合計を維持します。

おすすめ

転載: blog.csdn.net/m0_58367586/article/details/123727457