1. 程式人生 > >【藍橋杯】 歷屆試題 題解

【藍橋杯】 歷屆試題 題解

PREV-1

核桃的數量

問題描述

小張是軟體專案經理,他帶領3個開發組。工期緊,今天都在加班呢。為鼓舞士氣,小張打算給每個組發一袋核桃(據傳言能補腦)。他的要求是:

1. 各組的核桃數量必須相同

2. 各組內必須能平分核桃(當然是不能打碎的)

3. 儘量提供滿足1,2條件的最小數量(節約鬧革命嘛)

輸入格式 輸入包含三個正整數a, b, c,表示每個組正在加班的人數,用空格分開(a,b,c<30) 輸出格式 輸出一個正整數,表示每袋核桃的數量。 樣例輸入1 2 4 5 樣例輸出1 20 樣例輸入2 3 1 1 樣例輸出2 3

題意:求三個數的最小公倍數。

求兩個數的最小公倍數=兩個數的積/最大公約數,三個數同理。

#include <iostream>
#include<cstdio>

using namespace std;

int gcd(int x,int y)
{
    int r;
    while(r=x%y)    x=y,y=r;
    return y;
}

int main()
{
    int a,b,c;
    cin>>a>>b>>c;
    int ans=a*b/gcd(a,b);
    ans=ans*c/gcd(ans,c);
    cout<<ans<<endl;
}

PREV-2

列印十字圖

乍看沒規律,分解之後發現對稱。先找圖形的八分之一的規律,然後再形成全圖。

#include <iostream>
#include<cstdio>
#include<cstring>

using namespace std;

int a[210][210];

int main()
{
    int n;
    cin>>n;
    int m=n*2+3;
    memset(a,0,sizeof(a));
    for(int i=3;i<=m;i++)
    {
        for(int j=1;j<=i/2-1;j++)   a[i][j*2-1]=1;
        if(i%2)
            for(int j=i-2;j<=m;j++) a[i][j]=1;
    }
    for(int i=1;i<=m;i++)
        for(int j=i;j<=m;j++)
            a[i][j]=a[j][i];
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
        {
            a[m+i][j]=a[m-i][j];
            a[i][j+m]=a[i][m-j];
            a[i+m][j+m]=a[m-i][m-j];
        }
    for(int i=1;i<=2*m-1;i++)
    {
        for(int j=1;j<=2*m-1;j++)
            printf(a[i][j]==1?"$":".");
        cout<<endl;
    }
}

PREV-3

帶分數

問題描述

100 可以表示為帶分數的形式:100 = 3 + 69258 / 714。

還可以表示為:100 = 82 + 3546 / 197。

注意特徵:帶分數中,數字1~9分別出現且只出現一次(不包含0)。

類似這樣的帶分數,100 有 11 種表示法。

輸入格式

從標準輸入讀入一個正整數N (N<1000*1000)

輸出格式

程式輸出該數字用數碼1~9不重複不遺漏地組成帶分數表示的全部種數。

注意:不要求輸出每個表示,只統計有多少表示法!

樣例輸入1 100 樣例輸出1 11 樣例輸入2 105 樣例輸出2 6

把大於9876543+2/1 (n>9876544)的情況排除。可以用搜索做,先用dfs遍歷9個數的排列,再對每種排列方式插入 + 和 / ,統計滿足的情況。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;

int ans=0,num[10],n,d[10];

void judge()
{
    int sum=0;
    for(int i=1;i<=9;i++)
    {
        sum=sum*10+d[i];
        if(sum>=n)  return ;
        int fm=0;
        for(int j=i+1;j<=9;j++)
        {
            fm=fm*10+d[j];
            if(9-j>j-i) continue;
            int fz=0;
            for(int k=j+1;k<=9;k++) fz=fz*10+d[k];
            if(fz*(n-sum)==fm)
            {
                ans++;
                break;
            }
        }
    }
}

void dfs(int c)
{
    if(c==9)
    {
        judge();
        return;
    }
    for(int i=1;i<=9;i++)
    {
        if(!num[i])
        {
            num[i]=1;
            d[c+1]=i;
            dfs(c+1);
            num[i]=0;
        }
    }
}

int main()
{
    cin>>n;
    if(n>9876544)
    {
        cout<<0<<endl;
        return 0;
    }
    memset(num,0,sizeof(num));
    dfs(0);
    cout<<ans<<endl;
}

PREV-4

剪格子

問題描述

如下圖所示,3 x 3 的格子中填寫了一些整數。

+--*--+--+
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+

我們沿著圖中的星號線剪開,得到兩個部分,每個部分的數字和都是60。

本題的要求就是請你程式設計判定:對給定的m x n 的格子中的整數,是否可以分割為兩個部分,使得這兩個區域的數字和相等。

如果存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。

如果無法分割,則輸出 0。

輸入格式

程式先讀入兩個整數 m n 用空格分割 (m,n<10)。

表示表格的寬度和高度。

接下來是n行,每行m個正整數,用空格分開。每個整數不大於10000。

輸出格式 輸出一個整數,表示在所有解中,包含左上角的分割區可能包含的最小的格子數目。 樣例輸入1 3 3
10 1 52
20 30 1
1 2 3 樣例輸出1 3 樣例輸入2 4 3
1 1 1 1
1 30 80 2
1 1 1 100 樣例輸出2 10

dfs搜尋一遍,取最小的,注意回溯就行了。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define INF 0x7fffffff

using namespace std;

int n,m,ans,sum,d[4][2]={0,1,0,-1,1,0,-1,0},v[11][11],a[11][11];

void dfs(int x,int y,int s,int t)
{
    if(t>=ans||s>sum)    return ;
    if(s==sum)
    {
        ans=min(ans,t);
        return ;
    }
    for(int i=0;i<4;i++)
    {
        int tx=x+d[i][0],ty=y+d[i][1];
        if(tx<0||tx>=n||ty<0||ty>=m||v[tx][ty])    continue;
        v[tx][ty]=1;
        dfs(tx,ty,s+a[tx][ty],t+1);
        v[tx][ty]=0;
    }
}

int main()
{
    cin>>m>>n;
    sum=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            cin>>a[i][j];
            sum+=a[i][j];
        }
    if(sum%2)
    {
        cout<<0<<endl;
        return 0;
    }
    sum/=2;
    ans=INF;
    v[0][0]=1;
    dfs(0,0,a[0][0],1);
    if(ans==INF)    cout<<0<<endl;
    else    cout<<ans<<endl;
}

PREV-5

錯誤票據

問題描述

某涉密單位下發了某種票據,並要在年終全部收回。

每張票據有唯一的ID號。全年所有票據的ID號是連續的,但ID的開始數碼是隨機選定的。

因為工作人員疏忽,在錄入ID號的時候發生了一處錯誤,造成了某個ID斷號,另外一個ID重號。

你的任務是通過程式設計,找出斷號的ID和重號的ID。

假設斷號不可能發生在最大和最小號。

輸入格式

要求程式首先輸入一個整數N(N<100)表示後面資料行數。

接著讀入N行資料。

每行資料長度不等,是用空格分開的若干個(不大於100個)正整數(不大於100000),請注意行內和行末可能有多餘的空格,你的程式需要能處理這些空格。

每個整數代表一個ID號。

輸出格式

要求程式輸出1行,含兩個整數m n,用空格分隔。

其中,m表示斷號ID,n表示重號ID

樣例輸入1 2
5 6 8 11 9
10 12 9 樣例輸出1 7 9 樣例輸入2 6
164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196
172 189 127 107 112 192 103 131 133 169 158
128 102 110 148 139 157 140 195 197
185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190
149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188
113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119 樣例輸出2 105 120

sort水一下。

#include <iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

int a[11000];

int main()
{
    int N,n,cnt=0,m;
    cin>>N;
    while(~scanf("%d",&a[cnt])) cnt++;
    sort(a,a+cnt);
    for(int i=1;i<cnt;i++)
    {
        if(a[i]==a[i-1])    n=a[i];
        else if(a[i]!=a[i-1]+1)  m=a[i-1]+1;
    }
    cout<<m<<" "<<n<<endl;
}

PREV-6

翻硬幣

問題描述

小明正在玩一個“翻硬幣”的遊戲。

桌上放著排成一排的若干硬幣。我們用 * 表示正面,用 o 表示反面(是小寫字母,不是零)。

比如,可能情形是:**oo***oooo

如果同時翻轉左邊的兩個硬幣,則變為:oooo***oooo

現在小明的問題是:如果已知了初始狀態和要達到的目標狀態,每次只能同時翻轉相鄰的兩個硬幣,那麼對特定的局面,最少要翻動多少次呢?

我們約定:把翻動相鄰的兩個硬幣叫做一步操作,那麼要求:

輸入格式

兩行等長的字串,分別表示初始狀態和要達到的目標狀態。每行的長度<1000

輸出格式

一個整數,表示最小操作步數。

樣例輸入1 **********
o****o****
樣例輸出1 5 樣例輸入2 *o**o***o***
*o***o**o***
樣例輸出2 1 按順序一個個比較,不同就翻,把後面一個也翻一下。統計次數即可。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<cmath>

using namespace std;

int main()
{
    string s1,s2;
    cin>>s1>>s2;
    int len=s1.length();
    int ans=0;
    for(int i=0;i<len-1;i++)
    {
        if(s1[i]!=s2[i])
        {
            ans++;
            if(s2[i+1]=='*')    s2[i+1]='o';
            else    s2[i+1]='*';
        }
    }
    cout<<ans<<endl;
}

PREV-7

連號區間數

問題描述

小明這些天一直在思考這樣一個奇怪而有趣的問題:

在1~N的某個全排列中有多少個連號區間呢?這裡所說的連號區間的定義是:

如果區間[L, R] 裡的所有元素(即此排列的第L個到第R個元素)遞增排序後能得到一個長度為R-L+1的“連續”數列,則稱這個區間連號區間。

當N很小的時候,小明可以很快地算出答案,但是當N變大的時候,問題就不是那麼簡單了,現在小明需要你的幫助。

輸入格式

第一行是一個正整數N (1 <= N <= 50000), 表示全排列的規模。

第二行是N個不同的數字Pi(1 <= Pi <= N), 表示這N個數字的某一全排列。

輸出格式

輸出一個整數,表示不同連號區間的數目。

樣例輸入1 4
3 2 4 1
樣例輸出1 7 樣例輸入2 5
3 4 2 5 1
樣例輸出2 9 由題意判斷可知,如果區間[i,j]中的最大值max-最小值min=j-i,那麼這段區間為連號區間。
#include <iostream>
#include<cstdio>
#include<cmath>

using namespace std;

int a[55000];

int main()
{
    int n,ans=0,mmax,mmin;
    cin>>n;
    for(int i=0;i<n;i++)    scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
    {
        mmax=mmin=a[i];
        for(int j=i;j<n;j++)
        {
            mmin=min(mmin,a[j]);
            mmax=max(mmax,a[j]);
            if(mmax-mmin==j-i)  ans++;
        }
    }
    cout<<ans<<endl;
}

問題描述

小明開了一家糖果店。他別出心裁:把水果糖包成4顆一包和7顆一包的兩種。糖果不能拆包賣。

小朋友來買糖的時候,他就用這兩種包裝來組合。當然有些糖果數目是無法組合出來的,比如要買 10 顆糖。

你可以用計算機測試一下,在這種包裝情況下,最大不能買到的數量是17。大於17的任何數字都可以用4和7組合出來。

本題的要求就是在已知兩個包裝的數量時,求最大不能組合出的數字。

輸入格式

兩個正整數,表示每種包裝中糖的顆數(都不多於1000)

輸出格式

一個正整數,表示最大不能買到的糖數

樣例輸入1 4 7
樣例輸出1 17 樣例輸入2 3 5
樣例輸出2 7

我用完全揹包做的。後在網上看到大牛答案等於a*b-a-b直接得解,膜拜一下,數學好就是6。

#include <iostream>
#include<cstdio>

using namespace std;

int d[1100000];

int main()
{
    int a[2],ans=1;
    cin>>a[0]>>a[1];
    d[a[0]]=d[a[1]]=1;
    int mmax=a[0]*a[1];
    for(int i=1;i<=mmax;i++)
    {
        for(int j=0;j<=1;j++)
            if(a[j]<i)  d[i]+=d[i-a[j]];
        if(!d[i])   ans=i;
    }
    cout<<ans<<endl;
}

PREV-9

大臣的旅費

問題描述

很久以前,T王國空前繁榮。為了更好地管理國家,王國修建了大量的快速路,用於連線首都和王國內的各大城市。

為節省經費,T國的大臣們經過思考,制定了一套優秀的修建方案,使得任何一個大城市都能從首都直接或者通過其他大城市間接到達。同時,如果不重複經過大城市,從首都到達每個大城市的方案都是唯一的。

J是T國重要大臣,他巡查於各大城市之間,體察民情。所以,從一個城市馬不停蹄地到另一個城市成了J最常做的事情。他有一個錢袋,用於存放往來城市間的路費。

聰明的J發現,如果不在某個城市停下來修整,在連續行進過程中,他所花的路費與他已走過的距離有關,在走第x千米到第x+1千米這一千米中(x是整數),他花費的路費是x+10這麼多。也就是說走1千米花費11,走2千米要花費23。

J大臣想知道:他從某一個城市出發,中間不休息,到達另一個城市,所有可能花費的路費中最多是多少呢?

輸入格式

輸入的第一行包含一個整數n,表示包括首都在內的T王國的城市數

城市從1開始依次編號,1號城市為首都。

接下來n-1行,描述T國的高速路(T國的高速路一定是n-1條)

每行三個整數Pi, Qi, Di,表示城市Pi和城市Qi之間有一條高速路,長度為Di千米。

輸出格式

輸出一個整數,表示大臣J最多花費的路費是多少。

樣例輸入1 5
1 2 2
1 3 1
2 4 5
2 5 4
樣例輸出1 135 輸出格式

大臣J從城市4到城市5要花費135的路費。


兩遍dfs:

首先從u dfs找到最遠點v ,然後從v開始,dfs找到的最遠點一定是樹的直徑

證明:

如果u->v 和樹的直徑沒有公共點,則可以從樹的直徑終點到u引一條邊,樹直徑變長了,矛盾

假設交點為k,那麼k->v (或者就是v本身) 一定是樹直徑的一部分,(最優子結構)

這樣就證明了v一定在樹的直徑的端點處,(為什麼是端點,因為u->v是最遠的,一定是葉子節點)

#include <iostream>
#include<bits/stdc++.h>

using namespace std;

const int N=11000;
int cnt,head[N],l,n,v[N],r,next[N];

struct node
{
    int to,d,next;
}e[N*2];

void add(int p,int q,int d)
{
    e[cnt].to=q;
    e[cnt].d=d;
    e[cnt].next=head[p];
    head[p]=cnt++;
}

int dfs(int t)
{
    v[t]=1;
    int w=0;
    for(int i=head[t];i+1;i=e[i].next)
    {
        int q=e[i].to,d=e[i].d;
        if(!v[q])
        {
            int tw=dfs(q)+d;
            if(tw>w)
            {
                w=tw;
                next[t]=next[q];
            }
        }
    }
    return w;
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1;i<n;i++)
        {
            int p,q,d;
            scanf("%d%d%d",&p,&q,&d);
            add(p,q,d);
            add(q,p,d);
        }
        for(int i=1;i<=n;i++)   next[i]=i;
        memset(v,0,sizeof(v));
        dfs(1);
        memset(v,0,sizeof(v));
        int l=dfs(next[1]);

        int ans=l*10+(1+l)*l/2;
        printf("%d\n",ans);
    }
}


PREV-10

幸運數

問題描述

幸運數是波蘭數學家烏拉姆命名的。它採用與生成素數類似的“篩法”生成

首先從1開始寫出自然數1,2,3,4,5,6,....

1 就是第一個幸運數。

我們從2這個數開始。把所有序號能被2整除的項刪除,變為:

1 _ 3 _ 5 _ 7 _ 9 ....

把它們縮緊,重新記序,為:

1 3 5 7 9 .... 。這時,3為第2個幸運數,然後把所有能被3整除的序號位置的數刪去。注意,是序號位置,不是那個數本身能否被3整除!! 刪除的應該是5,11, 17, ...

此時7為第3個幸運數,然後再刪去序號位置能被7整除的(19,39,...)

最後剩下的序列類似:

1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, ...

輸入格式 輸入兩個正整數m n, 用空格分開 (m < n < 1000*1000) 輸出格式 程式輸出 位於m和n之間的幸運數的個數(不包含m和n)。 樣例輸入1 1 20 樣例輸出1 5 樣例輸入2 30 69 樣例輸出2 8

暴力模擬

#include <iostream>
#include<cstdio>

using namespace std;

int a[1100000],m,n;

void dfs(int s)
{
    int cnt=s;
    if(a[s]>=n)  return;
    for(int i=s;i<=n;i++)
        if((i+1)%a[s])  a[cnt++]=a[i];
    dfs(s+1);
}

int main()
{
    cin>>m>>n;
    for(int i=0;i<=n;i++) a[i]=i*2+1;
    dfs(1);
    int ans=0,i=0;
    while(a[i]<n)
        if(a[i++]>m)    ans++;
    cout<<ans<<endl;
}


問題描述

二叉樹可以用於排序。其原理很簡單:對於一個排序二叉樹新增新節點時,先與根節點比較,若小則交給左子樹繼續處理,否則交給右子樹。

當遇到空子樹時,則把該節點放入那個位置。

比如,10 8 5 7 12 4 的輸入順序,應該建成二叉樹如下圖所示,其中.表示空白。

...|-12
10-|
...|-8-|
.......|...|-7
.......|-5-|
...........|-4

本題目要求:根據已知的數字,建立排序二叉樹,並在標準輸出中橫向列印該二叉樹。

輸入格式

輸入資料為一行空格分開的N個整數。 N<100,每個數字不超過10000。

輸入資料中沒有重複的數字。

輸出格式

輸出該排序二叉樹的橫向表示。為了便於評卷程式比對空格的數目,請把空格用句點代替:

樣例輸入1 10 5 20 樣例輸出1 ...|-20
10-|
...|-5 樣例輸入2 5 10 20 8 4 7 樣例輸出2 .......|-20
..|-10-|
..|....|-8-|
..|........|-7
5-|
..|-4

這題搞得腦殼疼,模擬,先建樹再將影象存到陣列中,最後列印。具體見程式碼:

#include<bits/stdc++.h>

using namespace std;

const int N=110;

struct node
{
    int num,l,r;
}tree[N];

char mp[N][N*5];
int a[N],b[N],h[N],d[N],v[N],nh[N];

void dfs(int t,int m)
{
    if(t==0)
    {
        tree[t].num=a[t];
        tree[t].l=tree[t].r=t;
        return ;
    }
    if(a[t]>tree[m].num)
    {
        if(tree[m].r==m)
        {
            tree[m].r=t;
            tree[t].num=a[t];
            tree[t].l=tree[t].r=t;
        }
        else
        {
            dfs(t,tree[m].r);
        }
    }
    else
    {
        if(tree[m].l==m)
        {
            tree[m].l=t;
            tree[t].num=a[t];
            tree[t].l=tree[t].r=t;
        }
        else
        {
            dfs(t,tree[m].l);
        }
    }
}


int main()
{
    memset(v,0,sizeof(v));
    memset(mp,0,sizeof(mp));
    int n=0;
    char cq;
    while(~scanf("%d%c",&a[n++],&cq)&&cq!='\n');
    for(int i=0;i<n;i++)    b[i]=a[i];
    sort(b,b+n);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(a[i]==b[j])
            {
                h[i]=j;
                nh[j]=i;
                break;
            }
        }
    for(int i=0;i<n;i++)
    {
        dfs(i,0);
    }
    for(int i=0;i<n;i++)
    {
        if(i==0)
        {
            int t=a[i];
            int tn=0;
            int c[5];
            while(t)
            {
                c[tn++]=t%10;
                t/=10;
            }
            for(int j=0;j<tn;j++)
                mp[h[i]][j]=c[tn-j-1]+'0';
            //mp[h[i]][tn]='-';
            d[i]=tn;
        }
        else
        {
            int k=0;
            while(1)
            {
                if(tree[k].l==i||tree[k].r==i)  break;
                if(tree[k].num>a[i])    k=tree[k].l;
                else    k=tree[k].r;
            }
            v[k]=1;
            mp[h[k]][d[k]]='-';
            for(int j=min(h[i],h[k]);j<=max(h[i],h[k]);j++) mp[j][d[k]+1]='|';
            mp[h[i]][d[k]+2]='-';
            int t=a[i];
            int tn=0;
            int c[5];
            while(t)
            {
                c[tn++]=t%10;
                t/=10;
            }
            for(int j=0;j<tn;j++)
                mp[h[i]][d[k]+3+j]=c[tn-j-1]+'0';
            d[i]=d[k]+3+tn;
        }
    }//for(int i=0;i<n;i++)   cout<<h[i]<<" "<<d[i]<<endl;
    for(int i=n-1;i>=0;i--)
    {
        if(v[nh[i]])    d[nh[i]]+=2;
        for(int j=0;j<d[nh[i]];j++)
        {
            if(mp[i][j]==0)
            {
                mp[i][j]='.';
            }
            printf("%c",mp[i][j]);
        }
        printf("\n");
    }
}


PREV-12

危險係數

問題描述

抗日戰爭時期,冀中平原的地道戰曾發揮重要作用。

地道的多個站點間有通道連線,形成了龐大的網路。但也有隱患,當敵人發現了某個站點後,其它站點間可能因此會失去聯絡。

我們來定義一個危險係數DF(x,y):

對於兩個站點x和y (x != y), 如果能找到一個站點z,當z被敵人破壞後,x和y不連通,那麼我們稱z為關於x,y的關鍵點。相應的,對於任意一對站點x和y,危險係數DF(x,y)就表示為這兩點之間的關鍵點個數。

本題的任務是:已知網路結構,求兩站點之間的危險係數。

輸入格式

輸入資料第一行包含2個整數n(2 <= n <= 1000), m(0 <= m <= 2000),分別代表站點數,通道數;

接下來m行,每行兩個整數 u,v (1 <= u, v <= n; u != v)代表一條通道;

最後1行,兩個數u,v,代表詢問兩點之間的危險係數DF(u, v)。

輸出格式 一個整數,如果詢問的兩點不連通則輸出-1. 樣例輸入 7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6 樣例輸出 2

這題如果資料再大一些可以用tarjan做,但這裡n和m都這麼小,直接暴力可過:

#include <iostream>
#include<bits/stdc++.h>

using namespace std;
const int N=1100;
int vi,ui,cnt,n,m,head[N],vis[N];

struct node
{
    int next,v;
}e[N*2];

void add(int u,int v)
{
    e[cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

int dfs(int t)
{
    vis[t]=1;
    for(int i=head[t];i+1;i=e[i].next)
    {
        int v=e[i].v;
        if(!vis[v])
        {
            if(v==vi||dfs(v))   return 1;
        }
    }
    return 0;
}


int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        scanf("%d%d",&ui,&vi);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(i!=vi&&i!=ui)
            {
                memset(vis,0,sizeof(vis));
                vis[i]=1;
                if(dfs(ui)==0)   ans++;
            }
        }
        printf("%d\n",ans);
    }
}
PREV-13

網路尋路

問題描述

X 國的一個網路使用若干條線路連線若干個節點。節點間的通訊是雙向的。某重要資料包,為了安全起見,必須恰好被轉發兩次到達目的地。該包可能在任意一個節點產生,我們需要知道該網路中一共有多少種不同的轉發路徑。

源地址和目標地址可以相同,但中間節點必須不同。

如下圖所示的網路。

1 -> 2 -> 3 -> 1 是允許的

1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。

輸入格式

輸入資料的第一行為兩個整數N M,分別表示節點個數和連線線路的條數(1<=N<=10000; 0<=M<=100000)。

接下去有M行,每行為兩個整數 u 和 v,表示節點u 和 v 聯通(1<=u,v<=N , u!=v)。

輸入資料保證任意兩點最多隻有一條邊連線,並且沒有自己連自己的邊,即不存在重邊和自環。

輸出格式 輸出一個整數,表示滿足要求的路徑條數。 樣例輸入1 3 3
1 2
2 3
1 3 樣例輸出1 6 樣例輸入2 4 4
1 2
2 3
3 1
1 4 樣例輸出2 10

轉發兩次,可以看成三條邊相連,中間邊的端點的度必須大於等於2才行。對每一條邊,考慮當它為中間一條邊時有幾種情況。

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=110000;
int u[N],v[N],n,m;
ll d[N];


int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(d,0,sizeof(d));
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u[i],&v[i]);
            d[u[i]]++;
            d[v[i]]++;
        }
        ll ans=0;
        for(int i=0;i<m;i++)
        {
            ans+=(d[u[i]]-1)*(d[v[i]]-1);
        }
        printf("%lld\n",ans+ans);
    }
}

PREV-14

高僧鬥法

問題描述

  古時喪葬活動中經常請高僧做法事。儀式結束後,有時會有“高僧鬥法”的趣味節目,以舒緩壓抑的氣氛。
  節目大略步驟為:先用糧食(一般是稻米)在地上“畫”出若干級臺階(表示N級浮屠)。又有若干小和尚隨機地“站”在某個臺階上。最高一級臺階必須站人,其它任意。(如圖1所示)
  兩位參加遊戲的法師分別指揮某個小和尚向上走任意多級的臺階,但會被站在高階臺階上的小和尚阻擋,不能越過。兩個小和尚也不能站在同一臺階,也不能向低階臺階移動。
  兩法師輪流發出指令,最後所有小和尚必然會都擠在高段臺階,再也不能向上移動。輪到哪個法師指揮時無法繼續移動,則遊戲結束,該法師認輸。
  對於已知的臺階數和小和尚的分佈位置,請你計算先發指令的法師該如何決策才能保證勝出。

輸入格式

  輸入資料為一行用空格分開的N個整數,表示小和尚的位置。臺階序號從1算起,所以最後一個小和尚的位置即是臺階的總數。(N<100, 臺階總數<1000)

輸出格式

  輸出為一行用空格分開的兩個整數: A B, 表示把A位置的小和尚移動到B位置。若有多個解,輸出A值較小的解,若無解則輸出-1。

樣例輸入

1 5 9

樣例輸出

1 4

樣例輸入

1 5 8 10

樣例輸出

1 3

這題涉及到Nim博弈,推薦一篇部落格講的很清楚:http://www.cnblogs.com/exponent/articles/2141477.html

對這個問題,最大的難點就是把它轉換成Nim博弈的模型。也就是將其兩兩分組,將每組之間的差值看做石子的數量。移動每組低臺階的時候,石子數減少,移動每組高臺階的時候,石子數增加。你需要做的就是移動一個和尚,使石子數達到平衡局面。 這樣想就顯而易見了。對於奇數石子的處理方法是兩兩分組,捨去最高臺階(看成石子數為0)

#include <iostream>
#include<bits/stdc++.h>

using namespace std;

const int N=110;
int a[N];

int main()
{
    int n=0;
    while(~scanf("%d",&a[n]))   n++;
    sort(a,a+n);
    int sum=0,flag=0;
    for(int i=0;i<n-1;i++)
        if(i%2==0)
            sum^=(a[i+1]-a[i]-1);
    for(int i=0;i<n-1&&flag==0;i++)
    {
        for(int j=a[i]+1;j<a[i+1];j++)
        {
            if(i%2)
            {
                if((sum^(a[i]-a[i-1]-1)^(j-a[i-1]-1))==0)
                {
                    flag=1;
                    cout<<a[i]<<" "<<j<<endl;
                    break;
                }
            }
            else
            {
                if((sum^(a[i+1]-a[i]-1)^(a[i+1]-j-1))==0)
                {
                    flag=1;
                    cout<<a[i]<<" "<<j<<endl;
                    break;
                }
            }
        }
    }
    if(flag==0)cout<<-1<<endl;
}