貪欲(5)パンナップサック問題

目次

1.パンナップサック問題

第二に、OJの実際の戦闘

CSU 1775:悲しい動き

CSU 1926:最も少ないコインを使用する

CodeForces538B準バイナリ


1.パンナップサック問題

このタイプの欲張り問題は、ナップサック問題にいくぶん似ていますが、実際のナップサック問題ではありません。

本当のナップサック問題は基本的に全体的な計画の問題です。

 

第二に、OJの実際の戦闘

CSU 1775:悲しい動き

トピック:

説明

csuxushuにとって、CSU(California State University)に行くことができることは彼の人生の名誉であり、キャンパスを変更し、寮を移動することは彼の最大の不幸です。州の選択に成功したので、幸せなcsuxushuは今移動の準備をする必要があります。
勤勉なcsuxushuにとって、彼は本が多すぎるので、唯一の頭痛は本の取り扱いです。輸送中の本の破損を防ぐため、専用の収納ボックスをカスタマイズする予定です。簡単にするために、彼のすべての本は同じ仕様であると仮定します。マスカスタマイゼーションにより、これらの収納ボックスの長さは同じLで、幅と厚さは本の幅と厚さです。csuxushuには特別な要件があります。つまり、各収納ボックスには最大2冊の本しか収納できません。もちろん、彼はN冊すべての本を輸送する必要があり、各収納ボックスの本の全長はLを超えることはできません。
代表チームのメンバーはcsuxushuに多くの報酬を与えましたが、csuxushuはお金を節約するためにできるだけ少ない収納ボックスを節約したいと思っていましたが、このような困難な問題に直面したとき、彼は明らかに混乱していました。 CSUの最高のプログラマーが、彼がこの問題を解決するのを手伝ってくれました。

入力

最初の行の整数Tは、データのTグループを表し、その後に空白行が続き、各データセットの間に空白行があります。
データの各グループの最初の行は整数n(1≤n≤10^ 5)であり、これはn冊の本があることを意味します。2行目は整数L、1≤l≤10000で、これは特別な収納ボックスの長さです。次のn行は、各本の長さli、li≤lです。

出力

データの各グループは1行を出力します。つまり、少なくともcsuxushuが設定する必要のあるストレージボックスの数です。

サンプル入力

1

10
80
70
15
30
35
10
80
20
35
10
30

サンプル出力

6

このトピックも非常に退屈です。必要な出力形式は、2つの数値の間に空白行があることです。

このトピックは、最初にソートしてから欲張りアルゴリズムを使用することです。明確に言うのは難しいですが、アルゴリズムは正しいはずです。

コード:

#include<iostream>
#include<algorithm>
using namespace std;
 
int list[100000];
 
int main()
{
    int t, n, l;
    int low, high;
    int sum;
    cin >> t;
    for (int ii = 0; ii < t;ii++)
    {
        cin >> n >> l;
        for (int i = 0; i < n; i++)cin >> list[i];
        sort(list, list + n);
        low = 0;
        high = n - 1;
        sum = 0;
        while (low < high)
        {
            if (list[low] + list[high] <= l)low++;
            high--;
            sum++;
        }
        if (low == high)sum++;
        cout << sum << endl<<endl;
    }
    return 0;
}

CSU 1926:最も少ないコインを使用する

トピック:

説明

リトルXが住んでいる国では、多くの宗派がコインを持っています。リトルXの貯金箱には、5元、10元、20元、50元の4種類のコインがあります。彼が支払いたいとき、彼は常に最小量のコインを使いたいと思っています。リトルXが所有する4枚のコインの数を教えてください。リトルXが最適な支払いプランを見つけるのを手伝ってください(プランが存在しない可能性があります)。

入力

入力に含まれるデータセットは10個以下です。データの各グループは、スペース(0 <= A、B、C、D <= 1000000,1 <= S <= 10000000)で区切られた5つの整数A、B、C、D、およびSの行で構成されます。 small Xが所有する5元コイン、10元コイン、20元コイン、50元コインの数、およびsmall Xが現在支払う必要のある金額(単位:元)。

出力

データの各セットについて、小さいXを支払うことができない場合は、整数-1の行を出力します。それ以外の場合は、小さいXをそれぞれ表すスペースで区切られた5つの整数a、b、c、d、およびsの行を出力します。支払い計画で使用する必要があります5元コイン、10元コイン、20元コイン、50元コインの数、および小さなXが費やす必要のあるコインの総数。

サンプル入力

1 2 3 4 35
1 2 3 4 567

サンプル出力

1 1 1 0 3 
-1

コード:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
	int A, B, C, D, S;
	while (cin >> A >> B >> C >> D >> S)
	{
		if (S % 5 || S % 2 && A == 0)
		{
			cout << -1 << endl;
			continue;
		}
		S /= 5;  //1,2,4,10
		int a, b, c, d, s;
		if (A > S % 2 + 1 || B > 0)//贪心
		{
			d = min(S / 10, D), S -= d * 10;
			c = min(S / 4, C), S -= c * 4;
			b = min(S / 2, B), S -= b * 2;
			a = min(S, A), S -= a;
			s = a + b + c + d;
			if (S)cout << -1 << endl;
			else cout << a << " " << b << " " << c << " " << d << " " << s << endl;
			continue;
		}
		a = S % 2, S /= 2;//2,5
		if (S % 2 && D == 0)
		{
			cout << -1 << endl;
			continue;
		}
		d = min((S-S%2*5) / 10, (D - S % 2) / 2) * 2 + S % 2, S -= d * 5;
		c = min(S / 2, C), S -= c * 2;
		b = 0;
		s = a + b + c + d;
		if (S)cout << -1 << endl;
		else cout << a << " " << b << " " << c << " " << d << " " << s << endl;
	}
	return 0;
}

CodeForces538B準バイナリ

トピック:

説明

小数表現に数字0または1のみが含まれている場合、その数値は準バイナリと呼ばれます。たとえば、数値0、1、101、110011 —は準バイナリであり、数値2、12、900はそうではありません。

正の整数nが与えられ ます。準バイナリ数の最小数の合計として表します。

入力

最初の行は、単一の整数含ま N  (1≤  N  ≤106)。

出力

最初の行に、単一の整数 k を出力します。これは、準バイナリ数の合計として の数nの表現における最小数です 

2行目に、  k個の 数値(合計の要素)を出力します。これらの数値はすべて、上記の定義に従って準バイナリである必要があり、それらの合計はnに等しくなければなりません 数字の先頭のゼロを印刷する必要はありません。番号の順序は関係ありません。可能な表現が複数ある場合は、それらのいずれかを印刷できます。

サンプル入力

入力

9

出力

9 
1 1 1 1 1 1 1 1 1

入力

32

出力

3 
10 11 11

このテーマについては、最初に、いくつの数字を入力しても、求めている数字がこの数字に加算されることを明確にする必要があります。加算の過程で、キャリー現象が発生することはありません。

それを証明することは難しくありませんが、それは必要ではないはずです。

つまり、このプロパティを使用することで簡単に解決できます。

コード:

#include<iostream>
#include<stack>
using namespace std;
 
stack<int>s;
 
int ff(int a, int b, int c, int d, int e, int f)
{
	int min = 10;
	if (a > 0 && min > a)min = a;
	if (b > 0 && min > b)min = b;
	if (c > 0 && min > c)min = c;
	if (d > 0 && min > d)min = d;
	if (e > 0 && min > e)min = e;
	if (f > 0 && min > f)min = f;
	if (min == 10)return 0;
	int x = (a >= min) * 100000 + (b >= min) * 10000 + (c >= min) * 1000 + (d >= min) * 100 + (e >= min) * 10 + (f >= min);
	for (int i = 0; i < min; i++)s.push(x);
	return ff(a - (a >= min)*min, b - (b >= min)*min, c - (c >= min)*min, d - (d >= min)*min, e - (e >= min)*min, f - (f >= min)*min) + min;
}
 
int main()
{
	int n;
	cin >> n;
	int a, b, c, d, e, f;
	if (n == 1000000)cout << 1 << endl << 1000000;
	else
	{
		f = n % 10;
		e = n / 10 % 10;
		d = n / 100 % 10;
		c = n / 1000 % 10;
		b = n / 10000 % 10;
		a = n / 100000;
		cout << ff(a, b, c, d, e, f) << endl;
		while (!s.empty())
		{
			cout << s.top() << " ";
			s.pop();
		}
	}
	return 0;
}

もちろん、慎重に考えると、この質問にはスタックは必要ありません。

コード:

#include<iostream>
#include<stack>
using namespace std;
 
void ff(int a, int b, int c, int d, int e, int f)
{
	int s = 0;
	if (a + b + c + d + e + f == 0)return;
	if (f)
	{
		f--;
		s += 1;
	}
	if (e)
	{
		e--;
		s += 10;
	}
	if (d)
	{
		d--;
		s += 100;
	}
	if (c)
	{
		c--;
		s += 1000;
	}
	if (b)
	{
		b--;
		s += 10000;
	}
	if (a)
	{
		a--;
		s += 100000;
	}
	cout << s << " ";
	ff(a, b, c, d, e, f);
}
 
int main()
{
	int n;
	cin >> n;
	int a, b, c, d, e, f;
	if (n == 1000000)cout << 1 << endl << 1000000;
	else
	{
		f = n % 10;
		e = n / 10 % 10;
		d = n / 100 % 10;
		c = n / 1000 % 10;
		b = n / 10000 % 10;
		a = n / 100000;
		int max = 0;
		if (max < a)max = a;
		if (max < b)max = b;
		if (max < c)max = c;
		if (max < d)max = d;
		if (max < e)max = e;
		if (max < f)max = f;
		cout << max << endl;
		ff(a, b, c, d, e, f);
		cout << endl;
	}
	return 0;
}

このコードははるかに単純に見えます。

実際、違いは大きくありませんが、このコードは貪欲な戦略をより明確に反映しています。

もちろん、質問の条件を満たす場合は、できるだけ多くの1になるように、毎回数値を出力します。

おすすめ

転載: blog.csdn.net/nameofcsdn/article/details/112753449