グループの問題解決を改善NOIP2015

\(D1T1 \) 魔法の魔方陣\((OK)\)

\(D1T2 \) 情報転送\((OK)\)

\(D1T3 \) 地主\((OK)\)

\(D1T4 \) 地主拡張バージョンの\((OK)\)

\(D2T1 \) ジャンプ石\((OK)\)

\(D2T2 \) 部分文字列\((OK)\)

\(D2T3 \) 輸送計画

唯一の癌の質問は、\(NOIP \) 落胆年次一つの大きなデータ構造が存在します\を(?\)

\(D1T1 \)はちゃんと質問のシミュレーション意味を開始する最初の数から2次元配列を開きました。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
int a[40][40];
int main(){
    int n=read(),i=1,j=n/2+1;a[i][j]=1;
    for(int now=2;now<=n*n;++now){
        if(i==1&&j!=n){
            a[n][j+1]=now;
            i=n;j=j+1;continue;
        }
        if(j==n&&i!=1){
            a[i-1][1]=now;
            i=i-1;j=1;continue;
        }
        if(i==1&&j==n){
            a[2][j]=now;
            i=2;j=n;continue;
        }
        if(i!=1&&j!=n){
            if(!a[i-1][j+1]){
                a[i-1][j+1]=now;
                i=i-1;j=j+1;
            }
            else{
                a[i+1][j]=now;
                i=i+1;j=j;
            }
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j)cout<<a[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

\(D1T2 \)すべて最小リングフィギュアにそれを見つけるために、どのようにして、その長さの数値最小リングへの答えを知っていますか?これは有向グラフではないということに注意してください\(tarjan \)テンプレートのタイトルはまだ?そして、そこに\( DFS \)他のグッドプラクティス、ディスジョイントセットなど、私はしません。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=200005;
int a[N];
int tim,top,num,dfn[N],low[N],st[N],color[N],size[N];
inline void tarjan(int u){
    dfn[u]=low[u]=++tim;st[++top]=u;
    int v=a[u];
    if(!dfn[v]){
        tarjan(v);
        low[u]=min(low[u],low[v]);
    }
    else if(!color[v]){
        low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        color[u]=++num;
        ++size[num];
        while(st[top]!=u){
            color[st[top]]=num;
            ++size[num];--top;
        }
        --top;
    }
}
int main(){
    int n=read(),ans=n;
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    for(int i=1;i<=num;++i)if(size[i]>1)ans=min(ans,size[i]);
//注意不要把大小为1的强连通分量(环)算进去了
    printf("%d\n",ans);
    return 0;
}

\(D1T3 \)私はおそらく2〜3時間を行うこの質問、この議論にあるピット場所、地区のこの質問(つまり、できるだけまっすぐに長くないです\(!!! \) )。その後、少し注意検索(カード)の順序についてあなたはまっすぐダブルシングルストレートすべて何を置くとき。最後に、4の3は、各カードの残りの部分の上に出なければならない1または2のいずれかでした一つは、その後、直接の答えに蓄積数をカウントしますが、再度検索の上に毎回行っていない(T \)を\飛びます。

再びデータを直接送信し、コードの拡張バージョンをこの質問を書き込んだ後、私たちは、間違った二つの点、ダウンし、次のデータを(見つけああ、データプログラミングの顔に)、ポイントが発見された2発の爆弾が一度(すなわち、2つのペアを持つ爆弾)を終え、忘れ点があり、簡単に言えば、私は、カードの異なるタイプのカードでデフォルトの前に少し変更を書いたプログラムです。ただ細かいところ。

置か直接拡張バージョンの\(AC \)コード:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
int n,ans,sum[20];
inline int check3(){
    for(int i=3;i<=13;++i)
        if(sum[i]>=3&&sum[i+1]>=3)return i;
    return 0;
}
inline int check2(){
    for(int i=3;i<=12;++i)
        if(sum[i]>=2&&sum[i+1]>=2&&sum[i+2]>=2)return i;
    return 0;
}
inline int check_sz(){
    for(int i=3;i<=10;++i)
        if(sum[i]&&sum[i+1]&&sum[i+2]&&sum[i+3]&&sum[i+4])return i;
    return 0;
}
inline int check_zd(){
    for(int i=1;i<=14;++i)
        if(sum[i]==4)return i;
    return 0;
}
inline int check_san(){
    for(int i=1;i<=14;++i)
        if(sum[i]>=3)return i;
    return 0;
}
inline void dfs(int now){
    if(now>=ans)return;
    int sz=check_sz();
    if(sz){//单顺子
        int i=sz,cnt=0;
        for(;i<=15;++i){
            if(sum[i]){
                ++cnt;
                if(cnt>=5){//不是越长越好,所以每种合法长度都要搜
                    for(int j=sz;j<=i;++j)--sum[j];
                    dfs(now+1);
                    for(int j=sz;j<=i;++j)++sum[j];
                }
            }
            else break;
        }
    }
    int y=check2();
    if(y){//双顺子
        int i=y,cnt=0;
        for(;i<=15;++i){
            if(sum[i]>=2)sum[i]-=2,cnt+=2;
            else break;
        }
        dfs(now+1);
        for(int j=y;j<i;++j)sum[j]+=2;
    }
    int x=check3();
    if(x){//三顺子(不是飞机,不能带牌)
        int i=x,cnt=0;
        for(;i<=15;++i){
            if(sum[i]>=3)sum[i]-=3,cnt+=3;
            else break;
        }
        dfs(now+1);
        for(int j=x;j<i;++j)sum[j]+=3;
    }
    int san=check_san();
    if(san){//三个带一个或一对,一对不能是王炸
        sum[san]-=3;
        for(int i=2;i<=14;++i){
            if(sum[i]>=2){
                sum[i]-=2;
                dfs(now+1);
                sum[i]+=2;
            }
        }
        for(int i=0;i<=14;++i){
            if(sum[i]>=1){
                sum[i]-=1;
                dfs(now+1);
                sum[i]+=1;
            }
        }
        sum[san]+=3;
    }
    int zd=check_zd();
    if(zd){//四个带两个或两对,同样一对不能是王炸
        sum[zd]-=3;//四个可以先做3个的带
        for(int i=2;i<=14;++i){
            if(sum[i]>=2){
                sum[i]-=2;
                dfs(now+1);
                sum[i]+=2;
            }
        }
        for(int i=0;i<=14;++i){
            if(sum[i]>=1&&i!=zd){
                sum[i]-=1;
                dfs(now+1);
                sum[i]+=1;
            }
        }
        sum[zd]+=3;
        sum[zd]-=4;//再来搜索做4个的带
        for(int i=2;i<=14;++i){
            if(sum[i]>=2){
                sum[i]-=2;
                for(int j=i;j<=14;++j){
                    if(sum[j]>=2){
                        sum[j]-=2;
                        dfs(now+1);
                        sum[j]+=2;
                    }
                }
                sum[i]+=2;
            }
        }
        for(int i=0;i<=14;++i){
            if(sum[i]>=1){
                --sum[i];
                for(int j=i;j<=14;++j){
                    if(sum[j]>=1){
                        --sum[j];
                        dfs(now+1);
                        ++sum[j];
                    }
                }
                ++sum[i];
            }
        }
        sum[zd]+=4;
    }
    for(int i=0;i<=14;++i)if(sum[i])++now;
    ans=min(ans,now);
}
int main(){
    int T=read(),n=read();
    while(T--){
        for(int i=0;i<=15;++i)sum[i]=0;
        for(int i=1;i<=n;++i){
            int x=read(),y=read();
            if(x==1)x=14;++sum[x];
        }
        ans=n;dfs(0);printf("%d\n",ans);
    }
    return 0;
}

\(D2T1 \)まあ、それはテンプレートの質問への半分の答えです。半分の最大値、最小ジャンプ距離\(中旬\) 距離未満2石がある場合は、\(中旬\)その後、戻ってそれを置きます、石は(明らかに後者よりも優れて上記より)を除去します。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=50005;
int L,n,m,a[N],b[N];
inline bool check(int mid){
    for(int i=1;i<=n;++i)b[i]=a[i];b[n+1]=L;
    int cnt=0;
    for(int i=1;i<=n+1;++i){
        if(b[i]-b[i-1]<mid){
            ++cnt;b[i]=b[i-1];
        }
    }
    return cnt<=m;
}
int main(){
    L=read(),n=read(),m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    int l=0,r=L,ans,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

\(D2T2は\)良い\(DP \)は、自身が、午後はないでしょう作ら突然、問題の解決策を読んだ後ましょう\([I] [J F ] [K] [0/1]が\) 現在考慮表す\(\)の文字列(Iは\)\マッチング文字\(B \)文字列を最初の\(Jの\)文字、\(J \)文字が別々に分かれている\(K \)現在選出部、\(iは\)文字が選択/非選択の番号方式。

\(F [I] [J] [K] [0] = F [I-1] [J] [K] [0] + F [I-1] [J] [K] [1] \)、現在の\(私は\)先にあるものは何でも文字が選択されていない、そして、あなたの最初の\は、(i-1 \)文字を選択または転送を超える選択されていないことができます。

場合([I] = B [ J] \)\ 時間、\(F [I] [J] [K] [1] = F [I-1] [J-1] [K-1] [0 ] + F [I-1] [J-1] [K-1] [1] + F [I-1] [J-1] [K] [1] \)、現在選択された場合\を(私は\ )文字、およびちょうど合うように、この文字、単独のセグメントにこの文字は、その後、先にあるものは何でも、あなたの最初の場合は、\(I-1 \)文字は、選択したか(つまりから転送選択されていないことができ、[I-1 fは\( ] [1-J] [K-1] [0] + F [1-I] [J-1] [-K 1] [1] \))にセグメントに続く文字と現在の文字であれば、その文字の上に、必要な(即ち\(F [1-I] [1-J] [K] [1] \))。

場合\([i]が\)に等しくない\(B [j] \)場合、\(F [I] [J] [K] [1] = 0 \)

次いで、直接配列開く\を(1,000×1000 * 200 * 2 \)はルーチンに従って、オープン未満である(私は\ \()のみから(I-1 \)\の、一次元経時転送)のいずれかローリング又はロールアウト。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int mod=1e9+7;
const int N=1005;
int n,m,K;ll f[2][N][205][2];
char s1[N],s2[N];
int main(){
    n=read();m=read();K=read();
    scanf("%s%s",s1+1,s2+1);
    f[0][0][0][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=0;j<=min(i,m);++j){
            for(int k=0;k<=min(j,K);++k){
                f[i&1][j][k][0]=(f[(i-1)&1][j][k][0]+f[(i-1)&1][j][k][1])%mod;
                if(s1[i]==s2[j])f[i&1][j][k][1]=(f[(i-1)&1][j-1][k][1]+f[(i-1)&1][j-1][k-1][0]+f[(i-1)&1][j-1][k-1][1])%mod;
                else f[i&1][j][k][1]=0;
            }       
        }
    }
    printf("%lld\n",(f[n&1][m][K][0]+f[n&1][m][K][1])%mod);
    return 0;
}

おすすめ

転載: www.cnblogs.com/PPXppx/p/11779233.html