今週の学習:欲張りアルゴリズムとコード記述の詳細
1つ:欲張りアルゴリズム
アイデア:ローカル最適はグローバル最適に達します。
質問をするときの考え方:質問を見ると、それが貪欲な質問であることが一目でわかりません。最も基本的で基本的なアイデアでそれを調べてから、どこでそれを最適化できるかを考えます。 。徐々に、貪欲な考えを使うことができます。
質問に遭遇したとき、それは不可能です。
最初のステップでは、最初にテキストバージョンで質問の意味を読みます。
2番目のステップでは、数学を使用して条件を導出できるかどうかを見てみましょう。
3番目のステップ、いくつかの条件を導入できない場合は、csdnでのクラシックコードの実装を確認し、そのアイデアをウォークスルーして、この問題のアルゴリズムのアイデアをよりよく理解してから、に戻ります。最初のステップであり、アルゴリズムと質問の正しい理解を深め続けます。
4番目のステップは、今週使用した優先度付きキュー、STL、貪欲の詳細など、将来この問題に取り組むときに、この問題のアルゴリズムのアイデアを他の問題に組み込むことができるかどうかを確認することです。
最後のステップでは、この質問をより最適化するために、質問を行うときにこの質問の考え方を適切に変更できるかどうかを見てみましょう。
1:
思考プロセス:最初は単純すぎて、m * n枚のカードがあると思っていたのですが、mの倍数であれば着実にラウンドに勝つことができます。コードを書いたところ、結果が間違っていたので考えました。対戦相手は一度に1つのセルを数えるだけでなく、自分よりも多い数であれば負けてしまいます。対戦相手のmがあれば、もう一度考えます。 -1ハンドは私が失うよりも大きな私を持っているので、私が勝つ状況は1つだけです。つまり、私はそれらよりも多くのカードをプレイします。最大のカードからトラバースします。私のカードが彼らのカードよりも大きい場合、私はゲームに勝ちます。彼らのカードが大きい場合、彼らは一度私に勝つチャンスがあります。
実装コード1:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <queue>
#include <stack>
const int N=1e5;
using namespace std;
bool cmp(int a,int b)
{
return a>b;
}
int k=0,m,n,a[N],sum=0,cnt=0;
bool b[N];
int main()
{
while(cin>>m>>n&&m!=0)
{
sum=0;//对手比我牌大的个数
cnt=0;//我赢的次数
memset(b,0,sizeof(b));
for(int i=0;i<n;i++)
{
cin>>a[i];
b[a[i]]=1;//我的牌是1,对手的牌是0
}
for(int i=m*n;i>0;i--)
{
if(b[i])
{
if(sum==0)//如果对手的牌中没有大时
cnt++;
else
sum--;
}
else
sum++;
}
printf("Case %d: %d\n", ++k, cnt);
}
return 0;
実装コード2:
考え:私は少なくとも数回勝ち、多くても数回負けました。昇順は便利です。対戦相手の最小のカードが私の最小のカードよりも大きい場合、私はゲームに負けます。次に、この回数をnで引きます。これは、私が勝った回数です。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <queue>
#include <stack>
#include <cstdio>
const int maxn=50005;
using namespace std;
int m,n,k,sum,cnt,t=0,d;
int main()
{
int a[maxn];
bool used[maxn],vis[maxn];
while(cin>>m>>n&&m+n)
{
int ant=0;
memset(used,false,sizeof(used));
memset(vis,false,sizeof(vis));
for(int i=0;i<n;i++)
{
cin>>a[i];
vis[a[i]]=true;
}
sort(a,a+n);//升序排序
for(int i=1;i<m*n;i++)
{
if(!vis[i])
for(int j=0;j<n;j++)
if(!used[j])
{
if(a[j]<i)
{
used[j]=true;
ant++;
break;//这一步很重要,避免多次替换
}
}
}
printf("Case%d:%d\n",++t,n-ant);
}
return 0;
}
2:
n個のオブジェクトの質量が与えられると(2つのオブジェクトの質量がm1、m2であると仮定)、互いに衝突して2 sqrt(m1 m2)の質量のオブジェクトを形成します。2つのオブジェクトのみが衝突します。最終的な最小値を見つけます。質量。
考え:衝突したときに総質量を最小化するにはどうすればよいですか?そのとき、2つの大きな質量が衝突したと推測しましたが、その後、先生は数式を使用してこのアイデアの正しさを推測し、条件を使用できることを学びました質問では、条件を数式に変換し、一般的ないくつかのルールを見つけます。
式は次のように証明されます。質量が大きい場合
は3つのオブジェクト(a> b> c)の質量を想定し、
最初にタッチします。ans= sqrt(sqrt(2 a b)2 c)
質量が小さい場合は、最初にタッチします。 、ans = sqrt(sqrt(2 b c)2 a)
ここで、大きい方の質量に最初に触れると、大きい方の質量がより多くの正方形を開き、総質量が小さくなることがわかります。したがって、最初に大きい方の質量のタッチを使用してください。 。
でも、疑問があります。すべてのオブジェクトの中で、タッチ後の質量がどこにあるのかわかりません。オブジェクトの数も1つ減りました。衝突後、毎回並べ替えることができないので、考えました。優先順位:キューを一度使用した後、それは本当に使いやすく、多くの問題を解決するのに役立ちました。すべてのオブジェクトを優先キューに入れ、質量が最も大きい2つのオブジェクトを順番に取り出し、衝突後に優先キューに入れ、最後に1つのオブジェクトができるまで繰り返します。
実装コード
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <queue>
#include <stack>
const int maxn=110;
struct Node
{
double val;
bool operator < (const Node &a)const
{
return val<a.val;
}
};
int n;
priority_queue < Node > q;
int main()
{
while(scanf("%d",&n)!=EOF)
{
Node node;
for(int i=0;i<n;i++)
{
double val;
scanf("%lf",&val);
node.val=val;
q.push(node);
}
for(int i=1;i<n;i++)
{
double a=q.top().val;
q.pop();
double b=q.top().val;
q.pop();
node.val=2*sqrt(a*b);
q.push(node);
}
printf("%.3f\n",q.top().val);
q.pop();
}
return 0;
}
思考プロセス:締め切りが長い場合は迅速に行う必要があり、控除額が多すぎる場合は迅速に行う必要があります。当初は毎日行うことを考えていました。まず、控除額が最も多い日を終える必要があります。締め切りまでですが、後ろに控除があり(締め切りは1つで、1つしかできません)、前に行った控除よりも多くの控除がある科目がある場合は、前に置きます。終了したいのは、より多くの控除に置き換えられるため、最小の合計控除が得られます。
実装コード
#include <iostream>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <queue>
#include <stack>
#include <cstdio>
const int N=50005;
using namespace std;
struct node
{
int x,y;
bool v;
} a[N];
int m,n,k,sum,cnt;
bool cmp (node a,node b)
{
if(a.x!=b.x)
return a.x<b.x;
return a.y>b.y;
}
int main()
{
cin>>m;
while(m--)
{
sum=0,k=1;
cin>>n;
for(int i=0; i<n; i++)
{
cin>>a[i].x;
a[i].v=false;
}
for(int i=0; i<n; i++)
cin>>a[i].y;
sort(a,a+n,cmp);
for(int i=0; i<n; i++)
{
if(a[i].x>=k)
{
k++;
continue;
}
int t=i,temp=a[i].y;
for(int j=0; j<i; j++)
{
if(a[j].y<temp&&(!a[j].v))
{
t=j;
temp=a[j].y;
}
}
sum+=temp;
a[t].v=true;
}
cout<<sum<<endl;
}
return 0;
}
2:コード記述の詳細
1:質問の条件の一部としてブール型変数を使用する方法を知っている
2:質問に複数のケースがあるが、その数がわからない場合は、次の2つの方法を使用できます。
(1):while (scanf( "%d、&n)!= EOF)
(2):while(cin >> n)
3:質問に除算演算がある場合は、乗算などに変換してみてください
。4:いくつかの問題優先キュー:
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
5:数式をリストして自分自身をより意味のあるものにし、いくつかの有用な条件式を取得する方法を知っている
6:ACを確保するという条件の下で、コードを可能な限り最適化する
3:今週の学習体験
欲張りアルゴリズムの質問をするときにプレッシャーを感じました。他の人はとても速くしました。質問の意味さえ理解できない質問があるかもしれません。私が最初にそれらの質問を始めたとき、最初のいくつかは完全に理解できませんでした。欲張りの意味なので、質問をするのに時間がかかります。質問を理解するのに時間がかかることがあります。その結果、コードを書き終えてもACができません。それから、 csdnのソリューション。非常にシンプルで、最も基本的なソリューションであることがわかりました。特別な状況ではソリューションが実現できない可能性があるため、アルゴリズムの知識を徐々に使用して、ゆっくりと実行できるようにします。自分がやったことの無力さはありません。今週最も重要なことは、質問に関しては、どうすればよいかを理解することです。できない場合は、勉強を続ける必要があります。正直なところ、アルゴリズムの問題は非常に難しいです。ローカルで実行しているときに明らかに正しいデータがある場合もありますが、それを送信するのは間違った答えです。データが正確でないか、コードで解決できるのは一部の問題だけです。状況を解決するため、将来的には、コードを作成する前にすべての可能性を検討する必要があります。また、私は自分でコードを書く最も簡単な方法を使用して、直接ACの数式ほど良くないので、問題が発生したときに新しい発見があるかどうかを確認するためにいくつかの数式を作成する必要があります。次のステップは、いくつかの貪欲な質問をブラッシングし、あなたが学んだいくつかの小さな詳細に徐々に慣れることです。