對拍練習:選擇客棧(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函式裡的資料造的小一點,便於除錯,一直到無誤以後再除錯大資料。
希望可以對大家有幫助。謝謝。