4.28(TG模擬賽)總結

1.挖地雷

題目背景

NOIp1996提升組第三題ios

題目描述

在一個地圖上有N個地窖(N≤20),每一個地窖中埋有必定數量的地雷。同時,給出地窖之間的鏈接路徑。當地窖及其鏈接的數據給出以後,某人能夠從任一處開始挖地雷,而後能夠沿着指出的鏈接往下挖(僅能選擇一條路徑),當無鏈接時挖地雷工做結束。設計一個挖地雷的方案,使某人能挖到最多的地雷。c++

輸入輸出格式

輸入格式:

有若干行。git

第1行只有一個數字,表示地窖的個數N。數組

第2行有N個數,分別表示每一個地窖中的地雷個數。函數

第3行至第N+1行表示地窖之間的鏈接狀況:ui

第3行有n-1個數(0或1),表示第一個地窖至第2個、第3個、…、第n個地窖有否路徑鏈接。如第3行爲011000…0,則表示第1個地窖至第2個地窖有路徑,至第3個地窖有路徑,至第4個地窖、第5個、…、第n個地窖沒有路徑。spa

第4行有n−2個數,表示第二個地窖至第3個、第4個、…、第n個地窖有否路徑鏈接。設計

… …code

第n+1行有1個數,表示第n−1個地窖至第n個地窖有否路徑鏈接。(爲0表示沒有路徑,爲1表示有路徑)。blog

輸出格式:

有兩行

第一行表示挖得最多地雷時的挖地雷的順序,各地窖序號間以一個空格分隔,不得有多餘的空格。

第二行只有一個數,表示能挖到的最多地雷數。

輸入輸出樣例:

輸入樣例#1:

5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1

輸出樣例#1:

1 3 4 5
27

開始犯傻:

說實在的,考前,恰好打了一半這道題的dfs,可是考試以後打怎麼也不對,因而打了個一本通的dp,我好LJ。

(1)dfs的內心路程就是定義一個bool型函數判斷它還能不能挖,dfs函數部分判斷若是不能繼續挖下去,而且挖到的地雷數比以前的還多,就用opt記錄路徑pre,若是還能挖下去,就for循環找下一個能挖的地方,而後b改變標記,繼續dfs,注意回溯b的標記。(碼風可能有些奇怪

#include"bits/stdc++.h"
using namespace std;

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int n;
int ans=0;
#define N 22
int a[N];
int mp[N][N];
#define INF 0x3f3f3f3f
int pre[N];
bool b[N];
int cnt;
int opt[N];

int print(int x) {
    if(!pre[x]) cout<<pre[x]<<' ';
    else print(pre[x]);
}

bool check(int x) {
    for(int i=1; i<=n; i++)
        if(mp[x][i] && !b[i]) return false;
    return true;
}

void dfs(int x,int step,int sum) {
    if(check(x)) {
        if(sum>ans) {
            ans=sum;
            cnt=step;
            for(int i=1; i<=step; i++)
                opt[i]=pre[i];
        }
        return ;
    }
    for(int i=1; i<=n; i++) {
        if(!b[i] && mp[x][i]) {
            b[i]=1;
            pre[step+1]=i;
            dfs(i,step+1,sum+a[i]);
            b[i]=0;
        }
    }
}

void init() {
    for(int i=1; i<=n; i++) pre[i]=0;
    for(int i=1; i<=n; i++) b[i]=false;
}

int main() {
    n=read();
    init();
    for(int i=1; i<=n; i++) a[i]=read();
    for(int i=1; i<=n-1; i++) {
        for(int j=i+1; j<=n; j++) {
            cin>>mp[i][j];
        }
    }
    for(int i=1; i<=n; i++) {
        b[i]=1;
        pre[1]=i;
        dfs(i,1,a[i]);
        b[i]=0;
    }
    for(int i=1; i<=cnt; i++)
        cout<<opt[i]<<' ';
    cout<<endl<<ans;
    return 0;
}

(2)dp的內心路程是逆推,狀態轉移方程是f[i]=max{f[j]+h[i]}(mp[i][j]=1,i<j<=n)。

#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<cctype>
#include<list>
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 222
using namespace std;
int n;
int mp[N][N];
int h[N];
int f[N];
int ans=0;
int k=0;
int a[N];

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int main() {
    /*  freopen("lei.in","r",stdin);
        freopen("lei.out","w",stdout);*/
    n=read();
    for(int i=1; i<=n; i++) cin>>h[i],f[i]=h[i];
    int x,y;
    /*  while(scanf("%d",&x)!=0&&scanf("%d",&y)!=0) {
            mp[x][y]=1;
        }*/
    for(int i=1; i<=n; i++)
        for(int j=i+1; j<=n; j++) {
            int ww;
            cin>>ww;
            if(ww==1)
                mp[i][j]=1;
        }
    a[n]=0;
    for(int i=n-1; i>0; i--)
        for(int j=i+1; j<=n; j++)
            if(mp[i][j]&&f[j]+h[i]>f[i]) {
                f[i]=f[j]+h[i];
                a[i]=j;
            }
    for(int i=1; i<=n; i++)
        if(ans<f[i]) {
            ans=f[i];
            k=i;
        }
    cout<<k;
    k=a[k];
    while(k) {
        cout<<" "<<k;
        k=a[k];
    }
    cout<<"\n"<<ans<<'\n';
    return 0;
}

(有木有感受個人代碼很眼熟,是的,它就是一本通的嘿嘿Q


2.跳石頭

題目背景

一年一度的「跳石頭」比賽又要開始了!

題目描述

這項比賽將在一條筆直的河道中進行,河道中分佈着一些巨大岩石。組委會已經選擇好了兩塊岩石做爲比賽起點和終點。在起點和終點之間,有 N 塊岩石(不含起點和終點的岩石)。在比勝過程中,選手們將從起點出發,每一步跳向相鄰的岩石,直至到達終點。

爲了提升比賽難度,組委會計劃移走一些岩石,使得選手們在比勝過程中的最短跳躍距離儘量長。因爲預算限制,組委會至多從起點和終點之間移走 M 塊岩石(不能移走起點和終點的岩石)。

輸入輸出格式

輸入格式:

第一行包含三個整數L,N,M,分別表示起點到終點的距離,起點和終點之間的岩石數,以及組委會至多移走的岩石數。保證 \(L \geq 1\)\(N \geq M \geq 0\)
接下來 N 行,每行一個整數,第 i 行的整數\(D_i( 0 < D_i < L)\), 表示第i塊岩石與起點的距離。這些岩石按與起點距離從小到大的順序給出,且不會有兩個岩石出如今同一個位置。

輸出格式:

一個整數,即最短跳躍距離的最大值。

輸入輸出樣例:

輸入樣例#1:

25 5 2
2
11
14
17
21

輸出樣例#1:

4

說明:

輸入輸出樣例1說明:將與起點距離爲2和14的兩個岩石移走後,最短的跳躍距離爲 4(從與起點距離17的岩石跳到距離21的岩石,或者從距離21的岩石跳到終點)。

另:對於20%的數據,0 ≤ M ≤ N ≤ 10。

對於50%的數據,0 ≤ M ≤ N ≤ 100。

對於100%的數據,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

開始犯傻:

這道題用到了小a學長講的二分答案。
因而,在這裏獻醜啦。總結一下二分答案:

  1. 求最大的最小值
  1. 求最小的最大值
  1. 求知足條件下的最大(最小)值
  1. 求最靠近一個值的值
  1. 求最小的能知足條件的代價

附上一個接近萬能的二分模板:

int erfen(int x){
    int l=1,r=maxn,ans=0;
    while(l<=r){
        int mid=(l+r) >> 1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

再附上一位dalao的講解:

二分答案應該是在一個單調閉區間上進行的。也就是說,二分答案最後獲得的答案應該是一個肯定值,而不是像搜索那樣會出現多解。二分通常用來解決最優解問題。剛纔咱們說單調性,那麼這個單調性應該體如今哪裏呢?

能夠這樣想,在一個區間上,有不少數,這些數多是咱們這些問題的解,換句話說,這裏有不少不合法的解,也有不少合法的解。咱們只考慮合法解,並稱之爲可行解。考慮全部可行解,咱們確定是要從這些可行解中找到一個最好的做爲咱們的答案, 這個答案咱們稱之爲最優解。

最優解必定可行,但可行解不必定最優。咱們假設整個序列具備單調性,且一個數x爲可行解,那麼通常的,全部的x'(x'<x)都是可行解。而且,若是有一個數y是非法解,那麼通常的,全部的y'(y'>y)都是非法解。

接下來code就很好搞啦~
值得注意的一點是,它終點也是有石頭的~
check函數判斷,詳見代碼啦~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<cctype>
#include<list>
#define M 100099
#define INF 0x3f3f3f3f
using namespace std;
long long int dis;
int n,m;
int c[M];
int minn=INF;
int maxn=-INF;
int ans=0;

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int check(int x) {//爲何要用二分,由於具備單調性而且有界(騷~
    int num=0,now=0;//num已經搬走的,now目前的
    for(int i=1; i<=n+1; i++) {//要到終點那塊
        if(c[i]-now<x) {//若是比當前枚舉的最小距離還小就搬走
            num++;
        } else now=c[i];
    }
    if(num>m) return 0;//超過題目要求的
}

void erfen(int x,int y) {
    int l=0,r=dis;
    while(l<=r) {
        int mid=(l+r) >> 1;
        if(check(mid)) {
            l=mid+1;
            ans=mid;
        } else {
            r=mid-1;
        }
    }
}

int main() {
    /*freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);*/
    dis=read();
    n=read();
    m=read();
    int l=0,r=dis;
    for(int i=1; i<=n; i++) cin>>c[i];
    c[n+1]=dis;
    while(l<=r) {
        int mid=(l+r) >> 1;
        if(check(mid)) {
            l=mid+1;
            ans=mid;
        } else {
            r=mid-1;
        }
    }
    cout<<ans;
}

3.鋪設道路

題目描述

春春是一名道路工程師,負責鋪設一條長度爲 n 的道路。

鋪設道路的主要工做是填平下陷的地表。整段道路能夠看做是 n 塊首尾相連的區域,一開始,第 i 塊區域下陷的深度爲 \(d_i\)
春春天天能夠選擇一段連續區間[L,R],填充這段區間中的每塊區域,讓其下陷深度減小 1。在選擇區間時,須要保證,區間內的每塊區域在填充前下陷深度均不爲 0 。

春春但願你能幫他設計一種方案,能夠在最短的時間內將整段道路的下陷深度都變爲 0 。

輸入輸出格式

輸入格式:

輸入文件包含兩行,第一行包含一個整數 n,表示道路的長度。 第二行包含 n 個整數,相鄰兩數間用一個空格隔開,第 i 個整數爲 \(d_i\)

輸出格式:

輸出文件僅包含一個整數,即最少須要多少天才能完成任務。

輸入輸出樣例

輸入樣例#1:

6
4 3 2 5 3 5

輸出樣例#1:

9

說明

【樣例解釋】

一種可行的最佳方案是,依次選擇: [1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]。

【數據規模與約定】

對於 30% 的數據,1 ≤ n ≤ 10 ;
對於 70% 的數據,1 ≤ n ≤ 1000;
對於 100% 的數據,1 ≤ n ≤ 100000 , 0 ≤ \(d_i\) ≤ 10000。

開始犯傻:

這道題我竟然**似的沒看出來是原題(指積木大賽)(CCF:我抄我本身)。主要的內心路程仍是很善良的,主要的code也就是兩三行Q。咳咳,正經一點,兩種思路。

(1)貪心思路:若a[i]>a[i-1],計數器sum+=a[i]-a[i-1];

驗證:假設如今有一個坑,但旁邊又有一個坑。

你確定會選擇把兩個同時減1;

那麼小的坑確定會被大的坑「帶着」填掉。

大的坑也會減小a[i]-a[i-1]的深度,能夠說是「免費的」;

因此這樣貪心是對的;(來自dalao)

#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
long long ans=0;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)     cin>>a[i];
    for(int i=2;i<=n;i++)     if(a[i]>a[i-1]) ans+=a[i]-a[i-1];
    cout<<ans+a[1];
    return 0;
}

(2)遞推思路:
用f[i]表示前i個坑所鋪設的最少天數

那麼要作的只需比較一下當前的a[i](就是坑的深度)和a[i−1],分兩種狀況:

若是a[i]<=a[i-1],那麼在填a[i−1]時就能夠順便把a[i]填上,這樣顯然更優,因此f[i]=f[i-1];

不然的話,那麼在填a[i−1]時確定要儘可能把a[i]一塊填上,a[i]剩餘的就單獨填。。

因此,f[i]=f[i−1]+(a[i]−a[i−1])。

初始化f[1]=a[1],向後推就好了。(依舊來自dalao)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<stack>
#include<queue>
#include<map>
#include<list>
#include<set>
#include<cctype>
#include<bitset>
#define N 100111
int n,opt[N],uxv[N];

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int main(){
    //freopen("road.in","r",stdin); freopen("road.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) uxv[i]=read();
    opt[1]=uxv[1];
    for(int i=2;i<=n;i++) uxv[i]<=uxv[i-1] ? opt[i]=opt[i-1] : opt[i]=opt[i-1]+(uxv[i]-uxv[i-1]);
    printf("%d",opt[n]);
}

4.花匠

題目描述

花匠棟棟種了一排花,每株花都有本身的高度。花兒越長越大,也愈來愈擠。棟棟決定把這排中的一部分花移走,將剩下的留在原地,使得剩下的花能有空間長大,同時,棟棟但願剩下的花排列得比較別緻。

具體而言,棟棟的花的高度能夠當作一列整數\(h_1\),\(h_2\),...,\(h_n\)。設當一部分花被移走後,剩下的花的高度依次爲\(g_1\),\(g_2\),...,\(g_m\),則棟棟但願下面兩個條件中至少有一個知足:

條件 A:對於全部\(g_{2i}\)>\(g_{2i-1}\),\(g_{2i}\)>\(g_{2i+1}\)
條件 B:對於全部\(g_{2i}\)<\(g_{2i-1}\),\(g_{2i}\)<\(g_{2i+1}\)

注意上面兩個條件在m=1時同時知足,當m>1時最多有一個能知足。

請問,棟棟最多能將多少株花留在原地。

輸入輸出格式

輸入格式:

第一行包含一個整數n,表示開始時花的株數。

第二行包含n個整數,依次爲\(h_1\),\(h_2\),...,\(h_n\),表示每株花的高度。

輸出格式:

一個整數m,表示最多能留在原地的花的株數。

輸入輸出樣例

輸入樣例#1:

5
5 3 2 1 2

輸出樣例#1:

3

說明

【輸入輸出樣例說明】

有多種方法能夠正好保留3株花,例如,留下第一、四、5株,高度分別爲五、一、2,知足條件 B。

【數據範圍】

對於 20%的數據,n ≤ 10;

對於 30%的數據,n ≤ 25;

對於 70%的數據,n ≤ 1000,0 ≤ \(h_i\)≤ 1000;

對於 100%的數據,1≤n≤100,000,0≤\(h_i\)≤1,000,000,全部的\(h_i\)隨機生成,全部隨機數服從某區間內的均勻分佈。

開始犯傻:

考前作luogu上DP的題,而後翻題解的時候看到個雙倍經驗(眼前一亮),發現那道題的雙倍經驗就是這道,可是當時只是看了看題目,以爲真的是雙倍經驗,而後默默點了加入任務計劃。(我:MMP??)
廢話很少bb,畫個圖來解釋???(充滿善意的微笑)
此處輸入圖片的描述

而後吶,就差很少是這個意思。要再也不來解釋下樣例??(好的,又一次)

此處輸入圖片的描述

好的,差很少就是這個樣子。

而後就很是的好理解了,讓你找一個最長的大波浪序列,記錄下波谷波峯就好啦。(這是dalao的想法,如下才是個人)用兩個數組分別記錄到i降低和到i上升。
這是dalao的代碼:

#include<bits/stdc++.h>
using namespace std;
int n,h[1000005],ans=1;bool con;
int main()
{
    cin>>n;for(int i=1;i<=n;i++) cin>>h[i];
    if(h[2]>=h[1]) con=1;
    for(int i=1;i<=n;i++)
    {
        if(con==0&&i==n) {ans++;break;}
        if(con==1) if(h[i+1]<h[i]){ans++;con=0;continue;}
        if(con==0) if(h[i+1]>h[i]) {ans++;con=1;continue;}
    }
    cout<<ans; 
}

這是個人代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<cctype>
#include<list>
#define N 100055
using namespace std;
int h[N],f[N][3];

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int main() {
    /*freopen("flower.in","r",stdin);
    freopen("flower.out","w",stdin);*/
    int n,ans=0;
    n=read();
    memset(f,0,sizeof(f));
    for(int i=1; i<=n; i++) {
        h[i]=read();
        //  f[i][0]=f[i][4]=1;
    }
    f[1][0]=f[1][5]=1;
    for(int i=2; i<=n; i++) {
        if(h[i]>h[i-1])
            f[i][0]=f[i-1][6]+1;
        else f[i][0]=f[i-1][0];
        if(h[i]<h[i-1])
            f[i][7]=f[i-1][0]+1;
        else f[i][8]=f[i-1][9];
    }
    ans=max(f[n][0],f[n][10]);
    cout<<ans<<endl;
}

5.貨幣系統

題目描述

在網友的國度中共有 nn 種不一樣面額的貨幣,第 i 種貨幣的面額爲 a[i],你能夠假設每一種貨幣都有無窮多張。爲了方便,咱們把貨幣種數爲 n、面額數組爲 a[1..n] 的貨幣系統記做 (n,a)。

在一個完善的貨幣系統中,每個非負整數的金額 x 都應該能夠被表示出,即對每個非負整數 x,都存在 n 個非負整數 t[i] 知足 a[i] \times t[i]a[i]×t[i] 的和爲 xx。然而, 在網友的國度中,貨幣系統多是不完善的,便可能存在金額 xx 不能被該貨幣系統表示出。例如在貨幣系統 n=3n=3, a=[2,5,9]a=[2,5,9] 中,金額 1,31,3 就沒法被表示出來。

兩個貨幣系統 (n,a)(n,a) 和 (m,b)(m,b) 是等價的,當且僅當對於任意非負整數 xx,它要麼都可以被兩個貨幣系統表出,要麼不能被其中任何一個表出。

如今網友們打算簡化一下貨幣系統。他們但願找到一個貨幣系統 (m,b)(m,b),知足 (m,b)(m,b) 與原來的貨幣系統 (n,a)(n,a) 等價,且 mm 儘量的小。他們但願你來協助完成這個艱鉅的任務:找到最小的 mm。

輸入輸出格式
輸入格式:
輸入文件的第一行包含一個整數 TT,表示數據的組數。

接下來按照以下格式分別給出 TT 組數據。 每組數據的第一行包含一個正整數 nn。接下來一行包含 nn 個由空格隔開的正整數 a[i]a[i]。

輸出格式:
輸出文件共有 TT 行,對於每組數據,輸出一行一個正整數,表示全部與 (n,a)(n,a) 等價的貨幣系統 (m,b)(m,b) 中,最小的 mm。

輸入輸出樣例
輸入樣例#1:
2
4
3 19 10 6
5
11 29 13 19 17
輸出樣例#1:
2
5

說明

在第一組數據中,貨幣系統(2,[3,10])和給出的貨幣系統(n,a)等價,並能夠驗證不存在 m < 2 的等價的貨幣系統,所以答案爲 22。
在第二組數據中,能夠驗證不存在 m < n 的等價的貨幣系統,所以答案爲 5。

【數據範圍與約定】

[][11]
對於 100% 的數據,知足 1 ≤ T ≤ 20, n,a[i] ≥ 1。

開始犯傻:

考試時,題面我都沒搞懂,我是來搞笑的嘛Q?改題時:逐漸開始佩服我本身,仍是Orz一下咱們的loceaner大佬吧。

附上dalao的思路:

先解釋一下樣例1中第一組數據的3,10,19,6等價於3,10的緣由
6=3+3,19=3+3+3+10
即13,19均可以被湊出來
而第二組中的5個數都不能將其餘的數湊出來(或被湊出來)
因此直接輸出了5
因此就排序看看能不能湊出來就好啦
a[i]=0表示沒有i這個數,a[i]=1表示能夠湊出i這個數,a[i]=2表示自己就有i這個數
若是處理完成以後a[1~n]中還有等於2的(即自己就有這個數而且不能湊(只能他湊別人))
就讓ans++,最後輸出就行了

說實在的我看懂了,可是我不知道爲啥我打不出來,慘

附上dalao的代碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#define INF 0x3f3f3f3f
using namespace std;

int a[25001];
int b[101];
int t,n,ans=0;

inline int read() {
    char c=getchar();
    int x=0,f=1;
    while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} 
    while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    return x*f;
}

int main() {
    //freopen("money.in","r",stdin);
    //freopen("money.out","w",stdout);
    t=read();
    while (t--) {
        ans=0;
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        for (int i=1; i<=n; i++) {
            b[i]=read();
            a[b[i]]=2;//自己就有b[i]這個數;
        }
        sort(b+1,b+1+n);//從小到大排序 
        for (int i=1; i<=b[n]; i++) {
            if(a[i]>0) {//若是能夠湊出i
                        //那麼也能夠湊出i+b[j] 
                for(int j=1; j<=n; j++) {
                    if(i+b[j]<=b[n])//排序以後,b[n]必定是b數組中最大的數,在這裏防止越界 
                        a[i+b[j]]=1;
                    else break;//越界的話直接退出 
                }
            }
        }
        for(int i=1; i<=b[n]; i++)
            if(a[i]==2) ans++;//統計a[i]==2的個數輸出 
        printf("%d\n",ans);
    }
}

我太LJ了,因此代碼仍是看dalao的比較實在。


6.尋找道路

題目描述

在有向圖 G 中,每條邊的長度均爲1,現給定起點和終點,請你在圖中找一條從起點到終點的路徑,該路徑知足如下條件:

路徑上的全部點的出邊所指向的點都直接或間接與終點連通。
在知足條件1的狀況下使路徑最短。
注意:圖 G 中可能存在重邊和自環,題目保證終點沒有出邊。

請你輸出符合條件的路徑的長度。

輸入輸出格式

輸入格式:

第一行有兩個用一個空格隔開的整數 n 和 m,表示圖有 n 個點和 m 條邊。

接下來的 m 行每行 2 個整數 x,y之間用一個空格隔開,表示有一條邊從點 x 指向點y。

最後一行有兩個用一個空格隔開的整數 s, t表示起點爲 s,終點爲 t。

輸出格式:

輸出只有一行,包含一個整數,表示知足題目描述的最短路徑的長度。若是這樣的路徑不存在,輸出−1。

輸入輸出樣例

輸入樣例#1:

3 2
1 2
2 1
1 3

輸出樣例#1:

-1

輸入樣例#2:

6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5

輸出樣例#2:

3

說明

解釋1:

此處輸入圖片的描述
如上圖所示,箭頭表示有向道路,圓點表示城市。起點1與終點3不連通,因此知足題目描述的路徑不存在,故輸出−1 。

解釋2:

此處輸入圖片的描述
如上圖所示,知足條件的路徑爲1->3->4->5。注意點2不能在答案路徑中,由於點2連了一條邊到點6,而點6 不與終點5 連通。

【數據範圍】

對於30%的數據,0 < n ≤ 10,0 < m ≤ 20;

對於60%的數據,0 < n ≤ 100,0 < m ≤ 2000;

對於100%的數據,0 < n ≤ 10000 ,0 < m ≤ 200000,0 < x , y , s , t ≤ n,x,s ≠ t。

開始犯傻:

我不會這道題!!!
我不會這道!!!
我不會這!!!
我不會!!!
我不!!!
我!!!
!!!
!!

因此直接放代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<stack>
#include<queue>
#include<map>
#include<list>
#include<set>
#include<cctype>
#include<bitset>
#define N 20000
#define M 400000
#define Q 300000
#define INF 0x7fffffff
using namespace std;
struct edge {
    int u,v,next;
    //int num;
} a[M];
int head[N];
bool vis[N];
int num=0;
int n,m;
int s,t;
bool lala[N],mama[N];
int opt[N],uxv[N];

void add_edge(int x,int y) {
    a[++num].u=x;
    a[num].v=y;
    a[num].next=head[x];
    head[x]=num;
}

inline int read() {
    int num=0,f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    return num*f;
}

int SPFA(int x,int y) {
    int headd=-1,tail=0;
    for(int i=1; i<=n; i++) opt[i]=INF;
    opt[x]=0;
    uxv[0]=x;
    vis[x]=mama[x]=true;
    while(headd!=tail) {
        headd=(headd+1)%M;
        int uu=uxv[headd];
        int vv;
        for(int i=head[uu]; i; i=a[i].next)
            if(opt[uu]+1<opt[vv=a[i].v]) {
                opt[vv]=opt[uu]+1;
                if(!vis[vv]) {
                    tail=(tail+1)%M;
                    uxv[tail]=vv;
                    vis[vv]=true;
                    mama[vv]=true;
                }
            }
        vis[uu]=false;
    }
    if(opt[y]==INF) 
    return -1;
    return opt[y];
}

int main() {
    /*freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);*/
    n=read();
    m=read();
    for(int i=1; i<=m; i++) {
        int x,y;
        x=read();
        y=read();
        add_edge(y,x);
    }
    s=read();
    t=read();
    SPFA(t,n+1);
    for(int i=1; i<=n; i++) lala[i]=mama[i];
    for(int i=1; i<=m; i++)
        if(mama[a[i].u]==false)
            lala[a[i].v]=false;
    memset(head,0,sizeof(head));
    memset(vis,false,sizeof(vis));
    num=0;
    for(int i=1; i<=m; i++)
        if(lala[a[i].u] && lala[a[i].v])
            add_edge(a[i].v,a[i].u);
    printf("%d\n",SPFA(s,t));
    return 0;
}
綜上所述:我就是個弟弟。
相關文章
相關標籤/搜索