1. 程式人生 > >【HDU - 薛貓貓杯程式設計網路賽】【題解】

【HDU - 薛貓貓杯程式設計網路賽】【題解】

A

爬山

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

小Z準備去爬山,在他的面前有N座山,每座山都有對應的高度。他想選擇兩座高度差最小的山進行攀爬。但由於好多山之間的高度差可能是相同的,所以他需要你告訴他高度差最小的兩座山的高度差是多少以及有多少種不同的選取方式(選取山A、B和選取山B、A視為同一種選取方式)。

Input

輸入一個T,代表資料的組數。(T<=10)
每組資料包含一個N,代表有多少座山。(2<=N<=100,000)
接下來一行包括N個數,分別表示每座山的高度x(1<=x<=1000,000,000)。

Output

對於每組測試樣例,輸出一行,包含兩個整數,代表兩座山最小的高度,以及有多少種不同的方式選取兩座山。兩個數之間用一個空格隔開,每個測試樣例佔一行。

Sample Input

 

2 5 1 2 5 4 3 4 3 3 3 10

Sample Output

 

1 4 0 3

Hint

測試樣例包含2組樣例 第一組樣例有5座山,高度分別為1,2,5,4,3,所以高度差最小是1,有4種不同的選取方式, 分別為選取第一座山和第二座山、選取第二座山和第五座山、選取第四座山和第五座山、選取第三座山和第四座山。 第二組樣例有4座山,高度分別為3,3,3,10,所以高度差最小是0,有3種不同的選取方式, 分別為選取第一座山和第二座山、選取第二座山和第三座山、選取第一座山和第三座山。

解題報告:

  記得開longlong就好。。

AC程式碼:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<queue>
#include<stack>
#include<map> 
#include<string.h>
#define ll long long
#define inf 1e7
using namespace std;
ll a[200000];
map<ll,ll> mp;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        mp.clear();
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i]); 
            mp[a[i]]++;
        }
        sort(a,a+n);
        ll minn=2000000000;
        for(int i=n-1;i>=1;i--)
        {
            minn=min(minn,a[i]-a[i-1]);
        }
        ll ans=0;
        if(minn==0)
        for(int i=n-1;i>=1;i--)
        {
            if(mp[a[i]]>1)
            {
                ll te=mp[a[i]];
                ans+=te*(te-1)/2;
                mp[a[i]]=1;
            }
        }
        else
        for(int i=n-1;i>=1;i--)
        {
            if(minn==a[i]-a[i-1])
            {
                ans+=mp[a[i]]*mp[a[i-1]];
            }
        }
        printf("%lld %lld\n",minn,ans);
    }
    return 0;
} 

B

尋找消失的紙牌

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

在一個無聊的假期,小Z和他的夥伴準備鬥地主。紙牌拿出來除去大小王發現只有51張(實際應該有52張),小z想讓你幫忙找出消失的那張紙牌。紙牌的花色分別對應為A、B、C、D,紙牌的點數對應為1、2、3、4、5、6、7、8、9、10、J、Q、K。例如紅桃10對應為C10,梅花K對應為BK。

Input

第一行為一個整數T,代表有T組樣例。(T<=52)
每組資料中由51張牌組成,紙牌之間以空格隔開,牌順序打亂。每組資料佔一行。

Output

對於每個測試樣例,輸出消失的紙牌。每個測試樣例佔一行。

Sample Input

2

A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 AJ AQ B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 BJ BQ BK C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK

A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 AJ AQ AK B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 BJ BQ BK C1 C2 C3 C4 C5 C6 C8 C9 C10 CJ CQ CK D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK

Sample Output

AK C7

解題報告:

   set一發。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
set<string> ss;
int main() 
{
    char str[5];
    int t;
    cin>>t;
    while(t--) {
        ss.clear();
    ss.insert("A1");ss.insert("A2");ss.insert("A3");ss.insert("A4");ss.insert("A5");ss.insert("A6");ss.insert("A7");ss.insert("A8");ss.insert("A9");ss.insert("A10");ss.insert("AJ");ss.insert("AQ");ss.insert("B1");ss.insert("B2");ss.insert("B3");ss.insert("B4");ss.insert("B5");ss.insert("B6");ss.insert("B7");ss.insert("B8");ss.insert("B9");ss.insert("B10");ss.insert("BJ");ss.insert("BQ");ss.insert("BK");ss.insert("C1");ss.insert("C2");ss.insert("C3");ss.insert("C4");ss.insert("C5");ss.insert("C6");ss.insert("C7");ss.insert("C8");ss.insert("C9");ss.insert("C10");ss.insert("CJ");ss.insert("CQ");ss.insert("CK");ss.insert("D1");ss.insert("D2");ss.insert("D3");ss.insert("D4");ss.insert("D5");ss.insert("D6");ss.insert("D7");ss.insert("D8");ss.insert("D9");ss.insert("D10");ss.insert("DJ");ss.insert("DQ");ss.insert("DK");
    ss.insert("AK");        
        for(int i = 1; i<=51; i++) {
            scanf("%s",str);
            ss.erase(str);
        }
        cout << *ss.begin()<<endl;
    }

//    ss.insert("")
    return 0 ;
}

C

球球大作戰

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

小Z最近迷上了球球大作戰,他準備出一個與球球大作戰相似的題目來考考大家。現在有n個球依次排列在一條直線上,每個球有其對應的體積。每次合併操作可以將任意相鄰兩個球合併為一個球,合併之後的球的體積為這兩相鄰球體積之和。現在有m次合併,經過這m次合併之後,希望剩下球中體積的最小值能夠最大(採用最佳合併策略)。

Input

輸入一個T,代表資料的組數。(T<=10)
第二行包含兩個正整數N,M,表示N個球,M次合併機會。
接下來一行為n個正整數x[1], x[2], … ,x[n],其中x[i]表示編號為i的球的體積。
資料範圍:1≤M<N≤100000,1≤x[i]≤100000。

Output

對於每個測試樣例,輸出一行,包含一個整數,m次合併之後的剩下的球的體積的最小值最大是多少。每個測試樣例佔一行。

Sample Input

2 4 2 4 2 3 5 6 3 1 7 2 2 5 9

Sample Output

6 8 Hint: 第一組樣例: 合併4、2得到{ 6 3 5 },合併3、5得到{ 6 8 },最小值為6。 也可以這樣進行合併,合併2、3得到{ 4 5 5 },合併4、5得到{ 9 5 },最小值為5,但最小值小於上面的合併方案。 第二組樣例: 合併1、7得到 { 8 2 2 5 9 },合併2、2得到 { 8 4 5 9 },合併4、5得到 { 8 9 9 },最小值為8。

解題報告:

   二分最小值然後遍歷陣列就好了,,複雜度o(nlogn)、

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 200000 + 5;
ll a[MAX],b[MAX],n,m;
bool ok(ll x) {
    ll cnt = 0;
    for(int i = 1; i<=n+1; i++) b[i] = a[i];
    for(int i = 1; i<=n; i++) {
        if(b[i] < x) {
            b[i+1] = b[i]+b[i+1];
            cnt++;
        }
    }
    return cnt <= m; 
}
int main() 
{
    int t;
    cin>>t;
    while(t--) {
        ll sum = 0;
        scanf("%lld%lld",&n,&m);
        memset(a,0,sizeof a);
        for(int i = 1; i<=n; i++) scanf("%lld",a+i),sum += a[i];
        ll l = 0,r = sum;
        ll mid = (l+r)>>1,ans = 0;
        while(l<=r) {
            mid=(l+r)>>1;
            if(ok(mid)) {
                ans = mid;l=mid+1;
            } 
            else r=mid-1;
            
        }
        printf("%lld\n",ans);
    }
    
    return 0 ;
}
/*
1
6 3
1 7 2 2 5 9

*/

D

矩陣

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

有一個二維矩陣,長和寬分別是N,M。矩陣上的每個點有兩個狀態(0,1),問能不能找到一個K*K的子矩陣,子矩陣裡面每個點的狀態全為0?

Input

第一行為一個整數T,代表T組資料。(1=<T<=10)
第二行為三個整數N,M,K。(1<=N,M<=1000,1<=K<=min(N,M))
接下來N行,每行有M個整數,代表矩陣上對應的點的狀態,狀態只有0,1兩種。

Output

對於每個測試樣例,輸出一行,如果能找到子矩陣,輸出"Yes",否則輸出"No"。每個測試樣例佔一行。

Sample Input

2 3 3 2 1 0 0 1 0 0 1 1 1 3 3 2 1 0 0 0 1 0 0 0 0

Sample Output

Yes No

解題報告:

   裸的二維字首和、、

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
int a[1005][1005],n,m,k;
int sum[1005][1005];
int main() 
{
    int t;
    cin>>t;
    while(t--) {
        scanf("%d%d%d",&n,&m,&k);
        memset(a,0,sizeof a);
        for(int i = 1; i<=n; i++) {
            for(int j = 1; j<=m; j++) {
                scanf("%d",&a[i][j]);
            }
        }
        for(int i = 1; i<=n; i++) {
            for(int j = 1; j<=m; j++) {
                sum[i][j] = a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
            }
        }        
        int flag = 0;
        for(int i = k; i<=n; i++) {
            for(int j = k; j<=m; j++) {
                if(sum[i][j] - sum[i][j-k] - sum[i-k][j] + sum[i-k][j-k] == 0) {
                    flag=1;break;
                }
            }
            if(flag == 1) break;
        }
        if(flag) puts("Yes");
        else puts("No");

    }
    
    return 0 ;
}

E

序列專一

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

小Z是一個專一的人,他希望能把一個序列裡面的數也能夠唯一(即元素之間各不相同)。因為序列剛開始可能會有重複的數,所以他需要進行一些操作。每次操作可從序列中取出兩個數x,y,把max(x+y-1,max(x,y)-min(x,y)+2)放入序列中,再把x和y其中一個放入序列,丟棄另外一個。問至少需要操作多少次可以使序列裡面的數各不相同。

Input

輸入一個T,代表資料的組數。(T<=10)
每組資料包含一個N,代表這個序列有多少個數。(N<=100,000)
接下來一行包括N個數,分別表示序列中每個數的值x (1<=x<=100,000)。

Output

對於每個測試樣例,輸出一行,包含一個整數,代表至少需要操作多少次可以使序列裡面的數各不相同。每個測試樣例佔一行。

Sample Input

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

Sample Output

0 2 3 Hint: 資料輸入量較大,建議使用scanf,不要使用cin 第一組樣例不需要操作。 第二組樣例需要進行二次操作。第一次操作取出數2和5,丟棄2,放入5和6。第二次操作取出數3和5,丟棄3,放入5和7。最終的序列為2,3,4,5,6,7。 也可以這樣對第二組樣例進行操作。第一次操作取出數2和5,丟棄2,放入5和6。第二次操作取出數3和6,丟棄3,放入6和8。最終的序列為2,3,4,5,7,8。

解題報告:

   這題需要分析題目給出的式子,,會發現:給出一對(x,y)  總會得到一個max(x,y)的數、、所以操作的時候我們從大到小操作就一定可以,所以這題就變成找不同數的個數的題目。。

AC程式碼:

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<queue>
#include<stack>
#include<map> 
#include<string.h>
#define ll long long
#define inf 1e7
using namespace std;
int a[100100];
map<int,int>mp;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        mp.clear();
        priority_queue<int> que;
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            mp[a[i]]++;
        }
        int ans=0;
        for(int i=0;i<n;i++)
        {
            if(mp[a[i]]>1)
            {
                ans+=mp[a[i]]-1;
                mp[a[i]]=1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
} 

 

 


F

成語接龍

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

小Z和大Z最近沉迷於成語接龍遊戲,他們準備把成語接龍的規則修改一下。規則是這樣的:有兩個字串,如果第一個字串是第二個字串的子串(也就是第一個字串在第二個字串中可以找到),那麼第一個字串後面可以接第二個字串。問題來了,現在有n個字串,你可以把n個字串的順序進行重組,使得這n個字串可以成語接龍,即第一個字串後面可以接第二個字串,第二個字串後面可以接第三個字串,......,第n-1個字串後面可以跟第n個字串。問你能不能把n個字串順序重組,滿足這n個字串可以成語接龍。

Input

第一行為一個整數T,代表有T組樣例。(T<=10)
每組資料中:
第一行為一個整數N,表示有N個字串。(N<=100)
接下來n行,每行一個字串,每個字串長度小於等於100。

Output

對於每組測試樣例,如果這n個字串順序重排之後可以成語接龍,輸出“Yes”,否則輸出“No”。每個測試樣例佔一行。

Sample Input

3 5 Abcabc Abc Abca Abc A 2 ABAC ACB 2 ACDB ACB

Sample Output

Yes No No

Hint

第一組樣例的成語接龍順序可以為:A、Abc、Abc、Abca、Abcabc。

解題報告:

   剛開始讀錯題意、、以為都從首字元開始,想想字典樹?涼。直接o(n^2)找前驅?涼。WA兩發之後、、老老實實分情況了。。

AC程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
char tmp[105];
struct Node {
    char s[105];
    int len;
} node[105];
char str[105][105];
bool cmp(Node a,Node b) {
    if(a.len != b.len) return a.len < b.len;
    return strcmp(a.s,b.s)<0;
}
int main() 
{
    int t;
    cin>>t;
    while(t--) {
        int n;
        cin>>n;
        for(int i = 1; i<=n; i++) {
            scanf("%s",tmp);
            strcpy(node[i].s,tmp);
            node[i].len = strlen(tmp);
        }
        if(n == 1) {
            puts("Yes");continue;
        }
        sort(node+1,node+n+1,cmp);
        int flag = 1;
        for(int i = 1; i<n; i++) {
            if(node[i].len == node[i+1].len) {
                if(strcmp(node[i].s,node[i+1].s) != 0) flag = 0;break;
            }
        }
        if(flag == 0) {
            printf("No\n");
            continue;
        } 
        int top = 0;
        for(int i = 1; i<n; i++) {
            if(strcmp(node[i].s,node[i+1].s) != 0) {
                top++;
                strcpy(str[top],node[i].s);
            }
        }
        if(strcmp(node[n].s,node[n-1].s) !=0) {
            top++;
            strcpy(str[top],node[n].s);
        }
        for(int i = 2; i<=top; i++) {
            if(strstr(str[i],str[i-1]) == NULL) flag = 0;
        }
        if(flag) puts("Yes");
        else puts("No");


    }

    return 0 ;
}

 


H

拯救柳下惠的氣質

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

柳下惠是一個卓越的探險家,有一次,柳下惠在外探險的過程中,在一個N*M的迷宮中迷路了,現在柳下惠處於迷宮中的(x,y)點,迷宮的入口在(1,1)點。只有你沿著最短的路徑找到柳下惠,柳下惠才會跟你離開迷宮。在迷宮中,你可以走上下左右四個方向。問有多少種走法可以拯救柳下惠?答案對1000000007取模(每種走法只要有一步不同即視為不同)

Input

第一行有一個整數T,代表T組資料。(1<=T<=10)
每組資料的第一行有兩個整數N,M代表迷宮的大小。(2<=N,M<=500)
接下來N行每行有M個字元。(包括.和*,.代表可以行走的路,*代表不可以行走的牆。)
每組資料的最後一行有兩個整數x,y代表柳下惠現在的位置。(1<=x<=N,1<=y<=M)
(資料保證(1,1)和(x,y)的位置都是.,保證座標(x,y)不等於(1,1))

Output

對於每組資料,輸出一行。
如果可以拯救柳下惠,輸出可以拯救柳下惠的方案數,結果對1000000007取模。
如果不可以拯救柳下惠,輸出-1。

Sample Input

2 2 2 .. .. 2 2 2 2 .* *. 2 2

Sample Output

2 -1

解題報告:

   裸的最短路條數。。讀題啊別忘了取模啊。。寫著寫著就忘了、、WA了三發有點傷。。細節還是要要注意、、

AC程式碼:

#include <iostream>
#include<cstring>
#include<cmath> 
#include<cstdio>
#include<queue> 
#define ll long long
using namespace std;
 
int r,c,qiux,qiuy;
const int INF = 0x3f3f3f3f; 
const ll mod = 1000000007;
int dis[505][505];
char mm[505][505];
int maze[505][505];
ll ans[505][505];
bool vis[505][505];
int head[505];
int nx[4] = {0,1,0,-1};
int ny[4] = {1,0,-1,0};
struct Point {
    int x,y;
    int t;
    bool operator<(const Point & b) const {
        return t>b.t; 
    }
    Point(){}
    Point(int x,int y,int t):x(x),y(y),t(t) {}
}p;
void Dijkstra() {
    dis[1][1] = 0;
    ans[1][1] = 1;
    int minw = INF;
    priority_queue<Point > pq;
    pq.push(Point(1,1,0));
    while(!pq.empty()) {
        int nxx,nyy;
        //找點 
        Point cur = pq.top();pq.pop();
        if(vis[cur.x][cur.y] == 1) continue;
        vis[cur.x][cur.y] = 1;
        for(int k = 0; k<4; k++) {
            nxx = cur.x+nx[k];
            nyy = cur.y+ny[k];
            if(vis[nxx][nyy] == 1) continue;
            if(nxx<1 || nxx>r || nyy<1 || nyy>c) continue;
            int t = dis[cur.x][cur.y] + maze[nxx][nyy];
            if(dis[nxx][nyy] > t) {
                dis[nxx][nyy] = t;
                ans[nxx][nyy] = ans[cur.x][cur.y]%mod;
                pq.push(Point(nxx,nyy,dis[nxx][nyy]));
            }
            else if(dis[nxx][nyy] == t) {
                ans[nxx][nyy] += ans[cur.x][cur.y];
                ans[nxx][nyy] %= mod;
            }         
        }
    }
    if(dis[qiux][qiuy] == INF) printf("-1\n");
    else printf("%lld\n",ans[qiux][qiuy]%mod);
} 
 
void init() {
    memset(vis,0,sizeof(vis));
    memset(ans,0,sizeof ans);
    for(int i = 1; i<=r; i++) {
        for(int j = 1; j<=c; j++) {
            dis[i][j] = INF;
        }
    }
    memset(maze,INF,sizeof(maze));
}
int main()
{
    int t;
    cin>>t;
    while(t--) {
        scanf("%d%d",&r,&c); 
        init();
        for(int i = 1; i<=r; i++) {
            scanf("%s",mm[i]+1);
        }
        scanf("%d%d",&qiux,&qiuy);
        for(int i = 1; i<=r; i++) {
            for(int j = 1; j<=c; j++) {
                if(mm[i][j] == '.') maze[i][j] = 1;
            }
        }
        Dijkstra() ;
    }
    
    return 0 ;
}

G

三消遊戲

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0

Problem Description

程式設計大賽這樣的活動,報名選手自然少不了噹噹姐啦。作為太吾村裡最聰明的同學,她不一會兒便完成了比賽中的所有題目。酷愛三消遊戲的她順手在草稿紙上發明了一種簡單的數字三消遊戲。
遊戲中,每回合會給出一行n個數字,我們設其中第i個數字為a[i]。噹噹姐會把數字a[i]想象為一堆位於第i列的,高度為a[i]的積木(a[i]可以為0),而她每次操作,可以選擇下標連續(如3,4,5)的三個非空積木堆,在每個積木堆上都各拿走一塊積木。而如果再也找不到下標連續的三個非空積木堆,遊戲便結束了。
噹噹姐想要知道的是,最少操作多少次,以及最多操作多少次,可以使得遊戲結束呢?
注意,當一堆積木被消除為空時,所有積木的編號並不會發生改變。

Input

第一行為一個整數T,代表資料組數。
接下來,對於每組資料――
第一行兩個整數n,表示積木的堆數。
第二行有n個用空格隔開的整數,其中第i個數字a[i]代表第i列的積木個數。
資料保證――
1 <= T <= 1000
0 <= a[i] <= 500
設a[1] + a[2] + ... + a[n] = m,有:
對於98%的資料,3 <= n <= 10 && 0 <= m <= 100
對於100%的資料,3 <= n <= 100 && 0 <= m <= 1000

Output

對於每組資料,輸出兩個數字。
該行有2個整數,分別表示噹噹姐遊戲結束前最少的運算元和最多的運算元。

Sample Input

4 3 4 6 8 5 0 0 10 0 0 6 2 3 4 5 6 7 7 8 3 2 6 2 5 8

Sample Output

4 4 0 0 5 7 2 4

解題報告:

    不太會啊、、想著線段樹維護每個點的值,每次找2~n-1的最小值,減到0,順便更新兩側的值 再找最小值,重複操作直到最小值為0?然後涼了、、第三個樣例就不太行這樣做。

AC程式碼:

暫無、、、

 

(ps:部落格彙總有任何問題歡迎提出,一起討論)