sstream學習(~~留坑中。。。
~~EG題其實有涉及的)
模板 templete 體會。(留坑中。。。
)
eg題部分STL基礎
Where is the Marble? UVA - 10474(sort && lower_bound)
題意:
給你n個石頭,先排好序。
之後有m次詢問,問你x石頭是否存在,並在上面的有序表找到石頭的下標。
思路:
sort && 二分
反思:
好像 iterator it 沒有減法運算。(不能像數組一樣)
AC
#include <iostream>
#include <set>
#include <algorithm>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
//multiset<int>s;讀取不了位置,哎哎。
const int maxn=1e4+10;
int a[maxn];
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,m,kase=0;
while(cin>>n>>m,n|m)
{
//s.clear();
int x;
For(i,1,n)cin>>a[i];
//{
// cin>>x;
// s.insert(x);
//}
//multiset<int>::iterator it;
sort(a+1,a+1+n);
cout<<"CASE# "<<++kase<<":"<<'\n';
For(i,1,m)
{
cin>>x;
// it=s.lower_bound(x);//這裏的指針不能相減。
int pos=lower_bound(a+1,a+1+n,x)-a;
if(pos==n+1||a[pos]!=x)cout<<x<<" not found"<<"\n";
else cout<<x<<" found at "<<pos<<"\n";
// if(a[pos]==x)cout<<x<<" found at "<<pos<<"\n";
//紫薯的但是我覺得如果沒初始化,可能有bug
//else cout<<x<<" not found"<<"\n";
}
}
return 0;
}
The Blocks Problem UVA - 101(vector,我的太醜了,就放紫薯的吧)
題意:
從左到右有 n 個木塊,編號爲0~ n - 1, 要求模擬一下4種操作(下面的a和b都是編號)
- move a onto b: 把 a 和 b 上方的木塊全部歸位,然後把 a 摞到 b 上面
- move a over b: 把 a 上方的木塊全部歸位,然後把 a 放在 b 所在木塊堆的頂部
- pile a onto b: 把 b 上方的木塊全部歸位,然後把 a 及上面的木塊 摞到 b 上面
- pile a over b: 把 a 及上面的木塊 摞到 b 所在木塊堆的頂部
思路:
由於每個堆的高度 不確定,所以適合用 vector 來保存
開一個vector【】,去模擬,就像stack(棧一樣)。
反思:
- 本題有四個操作,如果完全獨立地處理各命令,代碼就會變得冗長而且容易出錯。更好地辦法是提取出指令間的共同點,編寫函數,減少重複代碼
- resize()有時也可以用來刪除數。
for(int i=h+1; i<pile[p].size(); i++)
{
int pos=pile[p][i];
pile[pos].pb(pos);
}
pile[p].resize(h+1);//delete.
for(int i=h; i<pile[p].size(); i++)
{
pile[p2].pb(pile[p][i]);
}
pile[p].resize(h);
-
==vector也可以下標訪問,==哎,用vector老是忘,直接去下標了。
-
比較字符可以在開頭定義cosnt
const string t1="onto";
const string t2="move";
筆記:
- vector 之間可以直接賦值,或者作爲函數的返回值
- 用empty測試是否爲空。
AC
#include <iostream>
#include <vector>
#include <string>
#define pb push_back
#define sz size()
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
vector<int>pile[25];
const string t1="onto";
const string t2="move";
//後面比較用的
int n,a,b;
void find_block(int a, int &p, int &h)
{
for(p=0; p<n; p++)
{
for(h=0;h<pile[p].size();h++)if(pile[p][h]==a)return;
}
}//找到木塊,返回高度,和在哪一堆。
void clear_above(int p ,int h)
{
for(int i=h+1; i<pile[p].size(); i++)
{
int pos=pile[p][i];
pile[pos].pb(pos);
}
pile[p].resize(h+1);//delete.
//最後再清除
}//清楚h上面的木塊,
void pile_onto(int p, int h, int p2)
{
for(int i=h; i<pile[p].size(); i++)
{
pile[p2].pb(pile[p][i]);
}
pile[p].resize(h);
}//把p高度h(可能有以上)移動到p2上
void printf()
{
For(i,0,n-1)
{
cout<<i<<":";
for(int j=0; j<pile[i].sz;j++)cout<<' '<<pile[i][j];
cout<<'\n';
}
}//輸出
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
string s1,s2;
For(i,0,n-1)pile[i].pb(i);
//cout<<"OK"<<endl;
while(cin>>s1&&s1[0]!='q')
{
cin>>a>>s2>>b;
// cout<<"Ok"<<endl;
int pa,pb2,ha,hb;
find_block(a,pa,ha);
find_block(b,pb2,hb);
if(pa==pb2)continue;
if(s2==t1)clear_above(pb2,hb);
if(s1==t2)clear_above(pa,ha);
pile_onto(pa,ha,pb2);
}
printf();
return 0;
}
Andy’s First Dictionary UVA - 10815(set)
題意:
有一個文本,要你找到所有的單詞,之後按照字典序輸出,不重複。(不區分大小寫)
思路:
set 有序的集合,
- 由於可能帶標點,所以把標點變成space,之後再用stringstream得到各個單詞。
if(isalpha(s[i]))s[i]=tolower(s[i]);
//判斷是否是字母,以及轉換爲小寫。
else s[i]=' ';
stringstream ss(s);
while(ss>>buf)text.insert(buf);//在ss中插入
反思:
- isalpha() 判斷是否是字母
- tolower(),轉換爲小寫。
- stringstream ss(s) 的操作
stringstream ss(s);
while(ss>>buf)text.insert(buf);//在ss中插入
- 讀到EOF可以 while(cin>>s)
AC
#include <iostream>
#include <string>
#include <sstream>
#include <set>
using namespace std;
set<string>text;
int main()
{
string s,buf;
while(cin>>s)
{
for(int i=0; i<s.size(); i++)
{
//if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z'))s[i]=tolower(s[i]);
if(isalpha(s[i]))s[i]=tolower(s[i]);
//判斷是否是字母,以及轉換爲小寫。
else s[i]=' ';
//把標點直接變成space
}
stringstream ss(s);
while(ss>>buf)text.insert(buf);//在ss中插入
}
set<string>::iterator it=text.begin();
for(it;it!=text.end();it++)cout<<*it<<'\n';
return 0;
}
18731 最接近的值
Description
查找特定的值是一種常見的操作,當數據量較大時,往往需要使用高效的結構和查找算法。
n個整數組成的序列,請輸出所有元素左側與它值最爲接近的值,我們定義“最接近”爲兩數之差的絕對值最小。
例如序列 5 1 4 2,1最接近值爲5,4最接近值爲5,2最接近值爲1。
特別的,第一個數的最接近值爲它自身。
如果一個數左側有兩個不同的值,絕對值差都是最小。例如3的左邊出現了1和5,我們認爲值較大的5爲最接近值
格式第一行一個整數n。(1<=n<=100000)
第二行n個整數,均爲int範圍。
輸出格式一行,n個整數,爲輸入序列對應元素的最接近值。
輸入樣例
4
5 1 4 2
輸出樣例
5 5 5 1
AC
#include <iostream>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int INF=0x3f3f3f3f;
set<int>s;
vector<int>ans;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,x,pos,temp,dis=0;
set<int>::iterator it;
cin>>n;
For(i,1,n)
{
dis=INF;
cin>>x;
if(i==1)
{
ans.push_back(x);
s.insert(x);
continue;
}
it=s.lower_bound(x);
if(it!=s.end()&&*it>=x)dis=*it-x,temp=*it;
if(it!=s.begin())
{
it--;//cout<<*it<<endl;
if(dis==INF||dis>x-*it)temp=*it;
}
ans.push_back(temp);
s.insert(x);
}
For(i,0,n-1)cout<<ans[i]<<' ';
return 0;
}
引進map前,先看看“經典數對”問題(其實有兩種統計方法,這裏只介紹其中一種,另外一種見(其實就數學)一道eg題)
下面的解法是邊讀邊算。另一種解法是先全部讀入再算。
18727 數對問題一
時間限制:1000MS 代碼長度限制:10KB(規模小,直接開數組)
Description
一個長度爲N的正整數序列,現在需要計算出有多少對數字的差的絕對值爲C。
注意只要位置不同就認爲是不相同的數對。
輸入格式第一行,兩個整數 N, C。(1=<N<=10000),(1=<C<=10000)
第二行,N個正整數a1…an。 (1=<ai<=10000)
輸出格式僅一行,滿足條件的數對的個數。
輸入樣例
4 1
1 2 3 1
輸出樣例
3
提示(a1,a2),(a2,a3),(a2,a4)共3個數對滿足條件。
#include <iostream>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int maxn=1e5+10;
int vis[maxn];
typedef long long ll;
int main()
{
ll ans=0;
int n,c,a;
cin>>n>>c;
For(i,1,n)
{
cin>>a;
vis[a]++;
if(a>=c)ans+=vis[a-c];
ans+=vis[a+c];
}
cout<<ans<<endl;
return 0;
}
18728 數對問題二
時間限制:1000MS 代碼長度限制:10KB(規模大,要開map)(set&&lower_bound)(東哥)
Description
此題目與數對問題一的唯一區別爲序列中元素的取值範圍。
一個長度爲N的正整數序列,現在需要計算出有多少對數字的差的絕對值爲C。
注意只要位置不同就認爲是不相同的數對。
輸入格式第一行,兩個整數 N, C。(1=<N<=10000),(1=<C<=10000)
第二行,N個正整數a1…an。 ai爲int範圍內的正整數。
輸出格式僅一行,滿足條件的數對的個數。
輸入樣例
4 1
1 2 3 1
輸出樣例
3
提示(a1,a2),(a2,a3),(a2,a4)共3個數對滿足條件。
#include <iostream>
#include <map>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef long long ll;
map<ll,ll>ma;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll ans=0,a;
int n,c;
cin>>n>>c;
For(i,1,n)
{
cin>>a;
if(a-c>=0&&ma[a-c])ans+=ma[a-c];
ans+=ma[a+c];
ma[a]++;
}
cout<<ans<<endl;
return 0;
}
Ananagrams UVA - 156(map)
題意:
輸入一個單詞文本,找出所有滿足下麪條件的單詞。該單詞不能通過字母重排,得到輸入文本中的另一個單詞。判斷是否滿足條件時,字母不分大小寫,但在輸入時應保留輸入文中的大小寫,按照字典序進行排列(所有大寫字母在小寫字母的前面)
思路:
- 本題說重排序,也就是說可以把每個單詞內排好,之後放在map裏記錄。
eg cat 的 自己排序就是,act,後面如果出現act,那麼cat肯定不是。 - 爲了完成1,可以先把原文本存起來,之後對現有的文本進行標準化。
反思
- 字符串本身就可以 sort()
- 當時做的時候我還手寫桶排,去排(哎哎哎啊)。
AC
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
vector<string>v;
vector<string>ans;
map<string,int>ma;
string stand(string s)
{
for(int i=0; i<s.size(); i++)s[i]=tolower(s[i]);
sort(s.begin(),s.end());
return s;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
string s;
while(cin>>s)
{
if(s[0]=='#')break;
v.push_back(s);
s=stand(s);
// cout<<s<<'\n';
if(!ma.count(s))ma[s]=0;
ma[s]++;
}
// for(int i=0; i<v.size(); i++)cout<<v[i]<<endl;
for(int i=0; i<v.size(); i++)
{
string temp=stand(v[i]);
if(ma[temp]==1)ans.push_back(v[i]);
}
sort(ans.begin(),ans.end());
for(int i=0; i<ans.size(); i++)cout<<ans[i]<<'\n';
return 0;
}
The SetStack Computer UVA - 12096(stack)
題意:
有一個專門爲了集合運算而設計的“集合棧”計算機。該機器有一個初始爲空的棧,並且支持以下操作。
- PUHS:空集“{ }“入棧。
- DUP:把當前棧頂元素複製一份再入棧。
- UNION: 出棧兩個集合,然後把兩者的並集入棧。
- INTERSECT:出棧兩個集合然後把兩者的交集入棧。
- ADD: 出棧兩個集合,然後把先出棧的集合加到後出棧的集合中,把結果入棧。
每次操作後,輸出棧頂集合的元素個數。
反思&題解
本題的集合並不是簡單的整數集合或者字符串集合,而是集合的集合。爲了方便起見,此處爲每一個不同的集合分配一個唯一的ID,則每個集合都可以表示成所包含元素的ID集合。(特殊地:空集的id爲0).
#define ALL(x) x.begin(), x.end();
//先寫兩個宏,方便後面的操作。
#define INS(x) inserter(x,x.being())
//這裏表示插入集合(inserter插入者)的宏、
- 對於集合交併可以調用:algorithm裏的
set_union(ALL(x1), ALL(x2), INS(x))
set_intersection(ALL(x1), ALL(x2), INS(x)) - 可以#define SET set< int >(後面調用set時,比較方便。)
- SET()可以表示空集。即(set< int >() 裏面放一個元素),空集套空集,也是有元素了。本題是先把空集壓入,之後就看是否會產生新集合,而新集合的產生只和操作345有關。而add操作是可以產生新集合的,如果空集套空集,就讓這個空集裏發一個元素0,表示有空集
由圖,可以發現,之前因爲空集,所以並沒有輸出,而當執行ADD後,即空集套空集,使得一個空集裏面有一個空集。(如果這個新集合放到一個新集合裏,那麼也是對應它的標號。)
AC
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <algorithm>
#define Set set<int>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
#define ALL(x) x.begin(),x.end()
//先寫兩個宏,方便後面的操作。
#define INS(x) inserter(x,x.begin())
//這裏表示插入集合(inserter**插入者**)的宏、
using namespace std;
map<Set,int>idcach;
//可以用map記錄一個集合是否出現過,如果出現過,就返回該集合的下標。
vector<Set>setcach;
//記錄集合的下標
int id(Set x)
{
if(idcach.count(x))return idcach[x];
setcach.push_back(x);
return idcach[x]=setcach.size()-1;//沒有出現過,賦值,並返回這個新集合的下標。
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
{
idcach.clear();
setcach.clear();
stack<int>st;//棧裏放集合(就可以轉換成放集合的下標了)
int n;
string s;
cin>>n;
For(i,1,n)
{
cin>>s;
if(s[0]=='P')st.push(id(Set()));
else if(s[0]=='D')st.push(st.top());
else
{
Set x1,x2,x;
x1=setcach[st.top()];st.pop();
x2=setcach[st.top()];st.pop();
if(s[0]=='U')set_union(ALL(x1),ALL(x2),INS(x));
else if(s[0]=='I')set_intersection(ALL(x1),ALL(x2),INS(x));
else {x=x2;x.insert(id(x1));}//把1這個集合的下標插入到集合2裏。
st.push(id(x));
}
// Set:: iterator it=setcach[st.top()].begin();
// for(it;it!=setcach[st.top()].end();it++)cout<<*it<<' ';
// cout<<endl;
cout<<setcach[st.top()].size()<<endl;
//每次輸出,就彈出下標(對應哪個集合),
//之後去setcach裏找到集合,之後返回這個集合元素的個數。
}
cout<<"***"<<endl;
}
return 0;
}
棧和排序
思路:
後綴維護+模擬
詳見
AC(1e6)
#include <iostream>
#include <stack>
#include <queue>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int maxn=1e5+10;
stack<int>st;
int n,x,a[maxn],suf[maxn];//,pre[maxn]maxx,vis[maxn],t,
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
For(i,1,n)cin>>a[i];
for(int i=n; i>=1; i--)
{
if(a[i]>suf[i+1])suf[i]=a[i];
else suf[i]=suf[i+1];
}
For(i,1,n)
{
x=a[i];
while(!st.empty()&&suf[i]<st.top())//suf【i】就是優化,省了0n查詢
{
cout<<st.top()<<' ';st.pop();
}
st.push(x);
}
while(!st.empty())//把剩下的輸出
{
cout<<st.top()<<' ';
st.pop();
}
return 0;
}
Team Queue UVA - 540(二維隊列)
思路:
簡單模擬。
- 建立一個團隊隊列,(只放團隊的編號)
- 每個團隊單獨看成一個隊列
- 在輸入的時候,用map把每個隊員的團隊都標價一下。
收穫:
- 隊列可以開二維
- while(cin>>T,T)(c++寫法,當T爲0時)
AC
#include <iostream>
#include <queue>
#include <map>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
int t;
queue<int>q[1010];
queue<int>qm;
map<int,int>ma;
void init()
{
while(!qm.empty())qm.pop();
For(i,1,t)while(!q[i].empty())q[i].pop();
ma.clear();
int num=0,x;
For(i,1,t)
{
cin>>num;
For(j,1,num)
{
cin>>x;
ma[x]=i;
}
}
}
void work()
{
string s;
while(cin>>s)
{
int x,num;
if(s[0]=='S')break;
else if(s[0]=='E')
{
cin>>x;
num=ma[x];
if(q[num].empty())qm.push(num);
q[num].push(x);
}
else
{
num=qm.front();
cout<<q[num].front()<<endl;
q[num].pop();
if(q[num].empty())qm.pop();
}
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int kase=0;
while(cin>>t,t)
{
cout<<"Scenario #"<<++kase<<endl;
init();
work();
cout<<endl;
}
return 0;
}
Ugly Numbers UVA - 136(優先隊列)
思路:
- 可以找到規律,對於任意醜數x:2x,3x,5x也是醜數。
- 這就可以轉換成隊列的問題了。
- 對於入隊的要標記這個醜數生成過
- 每次都拿最小的醜數去更新。
- 所以用優先隊列去維護。
反思:
- 不開longlong見祖宗
- 對於轉換成隊列的問題了。 這個過程還不是很熟練。
AC
#include <iostream>
#include <queue>
#include <map>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef long long ll;
const int cao[3]={2,3,5};
ll cnt=0,top;
map<ll,ll>vis;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
priority_queue<ll,vector<ll>,greater<ll> >q;
vis[1]=1;
q.push(1);
while(1)
{
top=q.top();
For(i,0,3-1)
{
if(!vis[cao[i]*top])
{
q.push(cao[i]*top);
vis[cao[i]*top]=1;
}
}
cnt++;
if(cnt==1500)break;
q.pop();
}
cout<<"The 1500'th ugly number is "<<q.top()<<'.'<<endl;
return 0;
}
Unix ls UVA - 400(sort&&string)
題意:
把輸入的n個串排序,再輸出。
思路:
找到最大串,maxx
之後
- col=(60-maxx)/(maxx+2)+1(向下取整)
- row=(n+col-1)/col(向上取整)
反思:
string 的 sort
- 對於string s【100】
不可以直接sort(s.begin(),s.end()) - 而要sort(s,s+n)
- 或者上vector< string >
紫薯一個地方強調好多次了,重複操作寫函數,減少錯誤的可能
AC
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
string s[110];
void print(const string&s,int len, char temp)//輸出的寫法
{
cout<<s;
for(int i=0; i<len-s.length(); i++)cout<<temp;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n;
while(cin>>n)
{
int maxx=0;
for(int i=0; i<n; i++)
{
cin>>s[i];
maxx=max(maxx,(int)(s[i].length() ));
}
sort(s,s+n);//這裏假如是vector就可以begin,end
print("",60,'-');
cout<<endl;
int col=(60-maxx)/(maxx+2)+1;
int row=(n+col-1)/col;
for(int r=0; r<row; r++)
{
for(int c=0; c<col; c++)
{
int indx=c*row+r;
if(indx>=n)continue;//夠了就別輸出了。
print(s[indx],(c==col-1)?maxx:maxx+2,' ');
}
cout<<endl;
}
//
}
return 0;
}
Database UVA - 1592(map&&string)
題意:
給你一個文本。要你看是否存在有四個string,
滿足:(r1,c1)=(r2,c1)和(r1,c2)=(r2,c2)
思路:
- 枚舉列c1,c2.
- 之後掃一遍行,如果當前二元組之前出現過,就NO。(map存二元組的行號)
- 最後都沒找到,就yes
反思:
map映射,編號
- 雖然二元組直接string也可以,但map中string的比較費時間(取決於string長度)。
- 所以可以先映射每個string爲int。之後就是int的比較(給string編號)
sstream的妙用(輸入
),(不過我沒用,直接暴力了哈哈)
這裏的 getline(input, temp, ‘,’) 後面那個’,‘元素是開區間。
getline(cin, line); stringstream input(line); // 字符串流
while (getline(input, temp, ',')) ;a[i][++cnt]=get_id(temp);
getline也要喫回車
ios關閉後,字符題有意想不到的結果
(用一次自閉一次
)
AC
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <cstring>
#include <cstdio>
#define mp make_pair
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef pair<int,int>pa;
map<pa,int>ma;
map<string,int>idcach;
vector<string>cach;
int get_id(string s)
{
if(idcach.count(s))return idcach[s];
cach.push_back(s);
return idcach[s]=cach.size()-1;
}
int a[10010][11],n,m,pre=0,cnt;
void init()
{
mst(a,0);
cach.clear();
idcach.clear();
For(i,1,n)
{
string temp,line;
pre=cnt=0;
getline(cin, line);
// cout<<line.length()<<endl;
for(int j=0; j<=line.length(); j++)
{
//int last=line.size()-1;
if(line[j]==','||j==line.length())
{
temp=line.substr(pre,j-pre);
cnt++;
a[i][cnt]=get_id(temp);
//cout<<temp<<' '<<get_id(temp)<<endl;
pre=j+1;
}
}
//cout<<endl;
}
}
void solve()
{
for(int i=1; i<=m; i++)
{
for(int j=i+1; j<=m; j++)
{
ma.clear();
for(int k=1; k<=n; k++)
{
if(ma.count(mp(a[k][i],a[k][j]) ) )
{
cout<<"NO"<<endl;
cout<<ma[mp(a[k][i],a[k][j])]<<' '<<k<<endl;
cout<<i<<' '<<j<<endl;
return ;
}
ma[mp(a[k][i],a[k][j])]=k;
}
}
}
cout<<"YES"<<endl;
return ;
}
int main()
{
//ios::sync_with_stdio(0);
//cin.tie(0);
//cout.tie(0);
//原來是標準輸入輸出的問題,吐了。一直讀取錯誤。(看來要標準輸入,輸出,讀字符串時)
while(cin>>n>>m)
{
// cout<<n<<' '<<m<<endl;
getchar();//!!!
init();
/*
For(i,1,n)
{
For(j,1,m)cout<<a[i][j]<<' ';
cout<<endl;
}
*/
solve();
}
return 0;
}
/*
3 3
How to compete in ACM ICPC,Peter,[email protected]
How to win ACM ICPC,Michael,[email protected]
Notes from ACM ICPC champion,Michael,[email protected]
2 3
1,Peter,[email protected]
2,Michael,[email protected]
*/
PGA Tour Prize Money UVA - 207(大模擬毒瘤題目,字符的讀入&&細節處理)
思路:
- 先把所有選手前兩輪的得分進行排名。(題目保證一定有70人晉級)
- 接下來四輪總分再進行一次排序。
- 之後就是頒獎。
- 最後輸出。
反思&&收穫:
本題的輸入一如既往要用getline
- 先喫回車:getchar。
- 之後讀取一行:getline(cin,line)
- 由於名字是要讀取21個字符,所以
play[i].name=line.substr(0,21);//輸入名字
- 業餘的判斷
play[i].amateur=(find(line.begin(),line.end(),'*')!=line.end());
- 把字符string轉換爲int
stoi
- 把int轉換爲string
to_string(int)
輸入X2
- 也可以把前20個給讀取了fgets(char*,size,stdin)
- 之後用scanf(”%s“)讀取。轉換就行。
輸出
printf(“%-10d“)輸出時後面不夠10.右補(很符合左+,右-
)
printf(”%10d“)輸出時,左邊不夠10,左補。
AC
#include <iostream>
#include <cstring>
#include <string>
#include <sstream>
#include <cstdio>
#include <algorithm>
#include <bits/stdc++.h>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int maxn=200;
const int maxt=50;
//const int DQ=999999;
const double eps=1e-8;
struct player
{
string name;
double money;
int place,all_2,all_4,run[4];//all_2存第一輪,all_4爲頒獎做準備
int run_num=0;
bool amateur=false,T=false,prize=false;
} play[maxn];
void newplay(player & a)
{
a.prize=a.amateur=a.T=a.run_num=a.all_2=a.all_4=0;
a.place=0;
a.money=0.0;
For(i,0,4-1)a.run[i]=-1;
}
bool cmp1(const player &a,const player &b)
{
return a.all_2<b.all_2;
}
bool cmp2(const player &a, const player &b)
{
if(a.run_num!=b.run_num)
return a.run_num>b.run_num;
else if(a.all_4!=b.all_4)
return a.all_4<b.all_4;
return a.name<b.name;
///use char[]
//strcmp(a.name,b.name)<0 a<b;
}
double purse[maxn];
int n;
void init()
{
For(i,0,70)cin>>purse[i];
cin>>n;
getchar();
string line;
For(i,1,n)
{
getline(cin,line);
newplay(play[i]);//初始化結構
play[i].amateur=(find(line.begin(),line.end(),'*')!=line.end());//判斷是否業餘。
//字符串查找
play[i].name=line.substr(0,21);//輸入名字
For(j,0,4-1)
{
string hole=line.substr(21+j*3,2);
if(hole=="DQ")
break;//如果DQ,那麼犯規,後面的得分沒有了。
play[i].run[j]=stoi(hole);
++play[i].run_num;
if(j<2)
play[i].all_2+=play[i].run[j];
play[i].all_4+=play[i].run[j];
}
play[i].all_2=(play[i].run_num<2)?400:play[i].all_2;//如果輪數小於兩輪,那麼肯定不可能獲獎,直接把它安排到最後面。
//因爲輸入保證有70人會進級
}
}
void divide()
{
int cnt=1;
for(int i=1; i<=n; )//For(i,1,n)
{
int j=i,profession=0;
for(; j<=n&&play[j].all_4==play[i].all_4; j++)
{
if(!play[j].amateur)
++profession,play[j].prize=(cnt<=70);
}//統計同名次,可能獲得獎金的人數
double cur_purse=0.0;
for(int k=0; k<profession&&cnt<=70; k++)
cur_purse+=purse[cnt++];
cur_purse*=purse[0]/100/profession;
For(k,i,j-1)
{
play[k].place=i;
play[k].money=play[k].prize?cur_purse:0.0;
play[k].T=!play[k].amateur&&profession>1&&play[k].prize;
}
i=j;
}
}
void print()
{
int cot=0;
printf("Player Name Place RD1 RD2 RD3 RD4 TOTAL Money Won\n");
printf("-----------------------------------------------------------------------\n");
For(i,1,n)
{
cout<<play[i].name;
string place="";
if(play[i].run_num==4)
place=to_string(play[i].place)+( (play[i].T)?"T":"");
printf("%-10s",place.c_str());
//if(play[i].place<10)cot=9;
//else if(play[i].place<100)cot=8;
//else cot=7;
//cout<<play[i].place;
//if(play[i].T&&play[i].run_num==4)cot--,cout<<"T";
// For(j,1,cot)cout<<' ';
For(j,0,4-1)
{
if(play[i].run[j]==-1)
cout<<" ";
else
printf("%-5d",play[i].run[j]);
}
if(play[i].run_num<4)
cout<<"DQ"<<'\n';
else if(!play[i].prize||play[i].amateur)
cout<<play[i].all_4<<'\n';
else
printf("%-10d$%9.2f\n",play[i].all_4,play[i].money);
}
}
int main()
{
int t,kase=0;//cout<<(0.0==0.00)<<endl;
cin>>t;
while(t--)
{
if(kase++)
cout<<endl;
init();
sort(play+1,play+1+n,cmp1);//the first cut
int temp=70;
while(temp+1<=n&&play[temp+1].all_2==play[70].all_2)
temp++;//找到70名以後,同名次的。
n=temp;
sort(play+1,play+1+n,cmp2);//篩選出可能獲獎者
divide();//開始頒獎
print();
}
return 0;
}
The Letter Carrier’s Rounds UVA - 814(又是一道模擬毒瘤,這裏放上紫薯的標程)
題意:
郵件格斯爲:user@MTA
當一個人以 user1@MTA1 發給另外一個人時user2@MTA2.這兩個MTA將會通信。
輸入:
- 先是每個MTA的用戶列表。
- 對於每個發送請求(輸入發送者和接收者列表),按順序輸出所有MTA之間的SMTP(簡單郵件協議)交互。(詳見題目)
- MTA1連接收件人的MTA2的順序應該與輸入“第一次出現的順序”一致。
思路:
- 對於第一次輸入每個MTA的用戶列表
(用一個set< string > addr,是爲了保存可能的用戶的郵箱地址,方便後面輸出時,查找是否有該用戶。addr.count(adress))放在有序表裏,我覺得而是爲了查找,方便
- 對於輸入,要根據郵箱地址,處理出收件人,(後面也有重複的操作),所以定義一個函數。
- map< string ,vector < string > >dest 定義,相當於把MTA1需要發送的用戶地址放入。
之後看用戶是否存在時,再與1中的set相比較,看是否有這個用戶。
- 還要定義一個vector< string > mta爲了存放MTA1要連接的其他MTA2.(這裏是爲了,題目的輸出要求
與輸入順序一致
),再定義一個map< string > vis去記錄,當前MTA,是否在vector裏 - 輸出時,遍歷vector < > mta即可。對於每個MTA1所連接的MTA,要看dest【MTA】裏存的用戶地址了。之後輸出時,要判斷用戶是否存在。
反思:
- map可以key爲string,而value爲一個數組。
- string可以不用初始化,直接加字符操作。也可以加換行。
- find函數,對於string 返回的好像可以是第幾個。而set裏,返回的是指針。
- 本題要小心空格的輸出。
AC(紫薯AC代碼)
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
using namespace std;
void parse_address(const string &s, string &user, string &mta)
{
int k=s.find("@");//找到“@”,並返回指向它的pos位置,
user=s.substr(0,k);//由於從0開始,所以可以這樣。
mta=s.substr(k+1);
}
int main()
{
int k;
string s,t,user1,mta1,user2,mta2;
set<string> adder;//存放所有的地址。
//
//輸入所有MTA,轉換爲地址。
while(cin>>s&&s!="*")//the first is MTA must be read.
{
cin>>s>>k;
while(k--){cin>>t;adder.insert(t+"@"+s);}
}
//solve
while(cin>>s&&s!="*")
{
parse_address(s,user1,mta1);//deal with the sender
//
vector<string>mta;//link all mta
map<string,vector<string> >dest;//where each MTA should send to.
set<string>vis;
while(cin>>t&&t!="*")
{
parse_address(t,user2,mta2);
if(vis.count(t))continue;
vis.insert(t);
if(!dest.count(mta2)){mta.push_back(mta2);dest[mta2]=vector<string>();}
dest[mta2].push_back(t);
}
getline(cin,t);//it can get "*" and "ENTER"
//
//read the text of the mail
string data;
while(getline(cin,t)&&t[0]!='*')data+=" "+t+"\n";
//
for(int i=0; i<mta.size(); i++)
{
string mta2=mta[i];
vector<string> users=dest[mta2];
cout<<"Connection between "<<mta1<<" and "<<mta2<<endl;
cout<<" HELO "<<mta1<<"\n";
cout<<" 250\n";
cout<<" MAIL FROM:<"<<s<<">\n";
cout<<" 250\n";
bool ok=false;
for(int j=0; j<users.size(); j++)
{
cout<<" RCPT TO:<"<<users[j]<<">\n";
if(adder.count(users[j])){ok=true;cout<<" 250\n";}
else cout<<" 550\n";
}
if(ok)
{
cout<<" DATA\n";
cout<<" 354\n";
cout<<data;
cout<<" .\n";
cout<<" 250\n";//
}//
cout<<" QUIT\n";
cout<<" 221\n";
}
}
return 0;
}
Urban ElevationsUVA - 221(離散化)
題意:
給你每個長方體的
座標,wide,depth,height。
叫你求可以看到幾個長方體(長方體之間沒有重疊)
思路:
- 數據小,可以直接暴力。
- 由於座標是實數,所以要離散化。(x座標有無窮多個,離散後,把無窮變爲有限)
- 先把輸入按x座標降序排序(相等就按y降序排)。
- 之後查看第一個立方體就是左小角的那個(俯視圖),那麼正視圖肯定可以看得到。先輸出這個立方體的編號。
- 之後對於每個長方體都查看
**兩個座標之間的中點**
是否可以看到(且沒有被擋住)。
AC
#include <iostream>
#include <algorithm>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
const int maxn=1e3+10;
struct rect
{
double x,y,w,d,h;
int id;
bool operator<(const rect &temp)const
{return x<temp.x||(x==temp.x&&y<temp.y);}
}b[maxn];
int n,cnt=0;
double x[maxn*2];
bool online(int i, double pos)
{
return b[i].x<=pos&&(b[i].x+b[i].w)>=pos;
}
bool check(int i, double pos)
{
if(!online(i,pos))return false;
For(k,1,n)
{
if(k==i)continue;
if(b[k].y<=b[i].y&&b[k].h>=b[i].h&&check(k,pos))return false;
}
return true;
}
int main()
{
int kase=0;
while(cin>>n,n)
{
For(i,1,n)
{
cin>>b[i].x>>b[i].y>>b[i].w>>b[i].d>>b[i].h;
x[cnt++]=b[i].x;
x[cnt++]=b[i].x+b[i].w;
b[i].id=i;
}
sort(x,x+cnt);
sort(b+1,b+1+n);
cnt=unique(x,x+cnt)-x;
if(kase)cout<<endl;
printf("For map #%d, the visible buildings are numbered as follows:\n%d",++kase,b[1].id);
For(i,2,n)
{
For(j,0,cnt-2)
{
double mid=(x[j]+x[j+1])/2;
if(check(i,mid))
{
cout<<" "<<b[i].id;
break;
}
}
}
cout<<'\n';
}
return 0;
}