1. 程式人生 > >對拍練習:選擇客棧(NOIP 2011)

對拍練習:選擇客棧(NOIP 2011)

題目描述

麗江河邊有n 家很有特色的客棧,客棧按照其位置順序從 1 到n 編號。每家客棧都按照某一種色調進行裝飾(總共 k 種,用整數 0 ~ k-1 表示),且每家客棧都設有一家咖啡店,每家咖啡店均有各自的最低消費。

兩位遊客一起去麗江旅遊,他們喜歡相同的色調,又想嘗試兩個不同的客棧,因此決定分別住在色調相同的兩家客棧中。晚上,他們打算選擇一家咖啡店喝咖啡,要求咖啡店位於兩人住的兩家客棧之間(包括他們住的客棧),且咖啡店的最低消費不超過 p 。

他們想知道總共有多少種選擇住宿的方案,保證晚上可以找到一家最低消費不超過 p元的咖啡店小聚。

輸入輸出格式
輸入格式:

輸入檔案hotel.in,共n+1 行。

第一行三個整數n ,k ,p,每兩個整數之間用一個空格隔開,分別表示客棧的個數,色調的數目和能接受的最低消費的最高值;

接下來的n 行,第 i+1 行兩個整數,之間用一個空格隔開,分別表示 i 號客棧的裝飾色調和i 號客棧的咖啡店的最低消費。

輸出格式:

輸出檔名為hotel.out 。

輸出只有一行,一個整數,表示可選的住宿方案的總數。

輸入輸出樣例

輸入樣例#1:
5 2 3
0 5
1 3
0 2
1 4
1 5

輸出樣例#1:
3

說明

【輸入輸出樣例說明】

這裡寫圖片描述

2 人要住同樣色調的客棧,所有可選的住宿方案包括:住客棧①③,②④,②⑤,④⑤,但是若選擇住4 、5 號客棧的話,4 、5 號客棧之間的咖啡店的最低消費是4 ,而兩人能承受的最低消費是3 元,所以不滿足要求。因此只有前 3 種方案可選。

【資料範圍】

對於30% 的資料,有 n ≤100;

對於50% 的資料,有 n ≤1,000;

對於100%的資料,有 2 ≤n ≤200,000,0 < k ≤50,0≤p ≤100 , 0 ≤最低消費≤100。

【exercise】
以前就寫過選擇客棧…感覺60分的O(n^3)暴力非常好打…但是這道題的細節處理比較多…於是我就用這道題練習對拍了
對拍步驟挺簡單:
1.寫一個正解
2.寫一個暴力
3.寫一個rand
4.寫一個對拍程式碼

【暴力】
先放一個通俗易懂的 暴力.cpp

//選擇客棧 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> #define M(a) memset(a,0,sizeof a) #define fo(i,j,k) for(i=j;i<=k;i++) using namespace std; const int T=200001; int n,k,p,mip,ans; int color[T],low[T],pre[51],shu[51]; int main() { freopen("rand.txt","r",stdin); //rand下文會提到 freopen("暴力.txt","w",stdout); int i,j,k; scanf("%d%d%d",&n,&k,&p); fo(i,1,n) { scanf("%d%d",&color[i],&low[i]); shu[color[i]]++; } fo(i,1,n) fo(j,i+1,n) if(color[i]==color[j]) { int l=i,r=j,mip=1e8; fo(l,i,j) { mip=min(mip,low[l]); if(mip<=p) { ans++; break; } } } cout<<ans; return 0; }

好…現在把暴力輸出到 暴力.txt 裡了

【正解】
現在我嘗試寫一下正解…

//選擇客棧正解 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=200005;
int n,m,p;
ll ans;
int c[mxn],a[mxn],num[100];
int main()
{
    freopen("rand.txt","r",stdin);
    freopen("hotel.txt","w",stdout);
    int i,j,k,ans=0;
    scanf("%d%d%d",&n,&m,&p);m--;
    fo(i,1,n)
      scanf("%d%d",&c[i],&a[i]),num[c[i]]++;
    fo(k,0,m)  //列舉顏色
    {
        int pre=0,sum=0;
        bool flag=0;
        fo(i,1,n) if(c[i]==k) break;
        fo(i,i,n)
        {
            if(a[i]<=p) flag=1;
              if(c[i]!=k) continue;
              pre++;sum++;
              if(a[i]<=p)
              {
                  ans+=(ll)(pre-1)*(ll)(num[k]-sum+1)+num[k]-sum;
                  pre=0;flag=0;
                  fo(i,i+1,n) if(c[i]==k) {i=i-1;break;}
              }
              else if(flag)
              {
                  ans+=(ll)(pre-1)*(ll)(num[k]-sum+1);
                  pre=0;flag=0;i--;sum--;
              }
        }
    }
    cout<<ans<<endl;
    return 0;
}

將正解輸出到 hotel.txt 裡去

【rand】

再看一下rand怎麼寫

//rand 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
int main()
{
    freopen("rand.txt","w",stdout);
    srand(time(0));  //這步必須加上,我也不知道為啥
    int n,m,p,i,j;
    n=rand()%1000+1;
    m=30;
    p=rand()%100;
    printf("%d %d %d\n",n,m,p);
    fo(i,1,n) printf("%d %d\n",rand()%30,rand()%100);
    return 0;
}

就是一堆隨機化…隨機出來的數輸出到一個檔案 rand.txt 裡去
注意標頭檔案要寫 ctime

【對拍】
最後看一下對拍函式
需要用到 cstdlib 標頭檔案

//對拍 
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
int n;
int main()
{
    while(1)
    {
        system("rand.exe");
        system("暴力.exe");
        system("hotel.exe");
        if(system("fc 暴力.txt hotel.txt ")) //fc是比較文字,如果文字相同,返回1
          while(1);
    }
    return 0;
}

這四個程式都是要執行的,先執行前三個,最後執行對拍

這裡寫圖片描述

雙擊對拍即可執行

【執行結果】

如果沒有差異就是這個樣子的~

完美

有差異的話就是下面這樣

這裡寫圖片描述

有差異的話說明正解有問題,這時候根據 rand.txt 裡的資料除錯自己的正解程式就好啦。一開始先把rand函式裡的資料造的小一點,便於除錯,一直到無誤以後再除錯大資料。

希望可以對大家有幫助。謝謝。