1 排座位
#include<iostream>
using namespace std;
const int N = 101;
int f[N],e[N][N],n,m,k;
int ffroot(int a){
while(a!=f[a]){
f[a]=f[f[a]];
a=f[a];
}
return a;
}
void iniarr(int arr[]){
for(int i=1;i<=n;i++){
arr[i] = i;
}
}
void friendly(int a,int b){
int fa = ffroot(a);
int fb = ffroot(b);
if(fa==fb) return ;
f[fa] = fb;
}
int main(){
ios::sync_with_stdio;
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k;
iniarr(f);
int x,y,z;
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
if(z==1){
friendly(x,y);
}
else{
e[x][y]=1;
e[y][x]=1;
}
}
for(int i=1;i<=k;i++){
cin>>x>>y;
if(ffroot(x)==ffroot(y)&&!e[x][y]){
cout<<"No problem\n";
}
else if(ffroot(x)!=ffroot(y)&&!e[x][y]){
cout<<"OK\n";
}
else if(ffroot(x)==ffroot(y)&&e[x][y]){
cout<<"OK but...\n";
}
else if(ffroot(x)!=ffroot(y)&&e[x][y]){
cout<<"No way\n";
}
}
}
思路:并查集问题。因为朋友的朋友是朋友,所以朋友之间用并查集;而敌人的判断直接用二维数组。最后的判断中总共有四种关系。
2 pSort
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int f[N],b[N],d[N],n;
int rk[N];
//用层数来优化
int root(int a){
while(a!=f[a]){
//优化2
f[a]=f[f[a]];
a=f[a];
}
return a;
}
void Union(int a,int b){
int ua = root(a);
int ub = root(b);
if(ua==ub) return ;
//注意一定是根进行变化!
if(rk[ua]>rk[ub]){
f[ub] = ua;
}
else if(rk[ub]>rk[ua]){
f[ua] = ub;
}
//优化:以高的树的根为根
else{
//ranka==rankb
rk[a]++;
f[ub] = ua;
}
}
int main(){
ios::sync_with_stdio;
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
f[i]=i;
cin>>b[i];
}
for(int i=1;i<=n;i++){
cin>>d[i];
if(i>d[i]){
Union(i,i-d[i]);
}
if(i+d[i]<=n){
Union(i,i+d[i]);
}
//交换条件:a数和b数的下标的差值=d[a]
//abs(i-j)==d[i];
}
for(int i=1;i<=n;i++){
if(root(i)!=root(b[i])){
cout<<"NO\n";
return 0;
}
}
cout<<"YES\n";
return 0;
}
思路:并查集问题。通俗来说(因为学识浅薄,没有证明过程),如果一个数列中的相邻两个数可以交换位置,数列就能够实现自由变形(全排列)。此题中下标的差值和 d [ i ] 的值确定,因此可交换的位置是固定的。通过Union将可交换的位置合并,则一个Union中的数列是可以任意变形的。那么,如果 b 数组中的某个数是在其下标的Union中的某个可到的位置,那么这个数就能被放在正确的位置。可以通过找根结点的方式判断是否在Union中。同时使用了两个优化,一个是根据rank保证树的不会变得太“高”,一个是根据找根函数中的处理进一步让树扁平化。
3 正整数A+B
先贴个错误代码,问题在输入上。
string a,b;
cin>>a>>b;
//输入两个
//这里输入b的时候会出现问题
//例如:2 3
//cin会在遇到空格时停止 scanf同理
以下为正确代码,使用 getline( cin , 字符串名 )即可读取空格。同时需要注意a,b中间还有一个空格,必须用getchar把它去掉。
#include<bits/stdc++.h>
using namespace std;
string a,b;
bool judge(string k){
bool flag=true;
for(int i=0;i<k.length();i++){
if(k[i]<'0'||k[i]>'9'){
flag=false;
break;
}
}
return flag;
}
int tran(string k){
int sum=0;
for(int i=0;i<k.size();i++){
sum*=10;
sum+=k[i]-'0';
}
return sum;
}
int main(){
bool fa=false,fb=false;
cin>>a;
getchar();
getline(cin,b);
if(judge(a)&&tran(a)>=1&&tran(a)<=1000){
fa=true;
cout<<tran(a)<<" + ";
}
else{
cout<<"? + ";
}
if(judge(b)&&tran(b)>=1&&tran(b)<=1000){
fb=true;
cout<<tran(b)<<" = ";
}
else{
cout<<"? = ";
}
if(fa&&fb){
cout<<tran(a)+tran(b);
}
else{
cout<<"?";
}
}
4 机工士姆斯塔迪奥
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int N,M,Q,roc,row,column,tmp;
//row or colum
bool R[MAXN],C[MAXN];
int main(){
cin>>N>>M>>Q;
while(Q--){
cin>>roc>>tmp;
if(roc == 0&&R[tmp]==true||roc==1&&C[tmp]==true){
continue;
}
if(roc == 0){
R[tmp] = true;
row ++;
}
else if(roc == 1){
C[tmp] = true;
column ++;
}
}
cout<<N*M-column*N-row*M+column*row;
}
思路:这是一道结论题。
5 名人堂与代金券
#include<bits/stdc++.h>
using namespace std;
const int N = 10005;
//结构体
struct mooc{
string name; //账号名
int score; //成绩
int id; //名次
}m[N];
//判断先后,注意字母序升序
bool cmp(mooc a,mooc b){
if(a.score != b.score){
return a.score > b.score;
}
else{
return a.name < b.name;
}
}
int n,g,k,sco,ans,cnt;
string nam;
int main(){
ios::sync_with_stdio;
cin.tie(0);
cout.tie(0);
cin>>n>>g>>k;
for(int i = 1; i <= n; i++){
cin>>nam>>sco;
m[i].name = nam;
m[i].score = sco;
if(sco >= g) ans += 50;
else if(sco >= 60 && sco < g) ans +=20;
}
//输出:代金券总值
cout<<ans<<endl;
sort(m+1,m+1+n,cmp);
for(int i = 1; i <= n; i++){
//注意有并列
cnt++;
//第一项单独赋值
if(i == 1){
m[i].id = cnt;
}
else if(m[i].score == m[i-1].score){
m[i].id = m[i-1].id;
}
else{
m[i].id = cnt;
}
//输出排名在 k 之前的
if(m[i].id<=k) cout<<m[i].id<<" "<<m[i].name<<" "<<m[i].score<<endl;
else break;
}
return 0;
}
思路:使用结构体和 sort 函数。
6 包装机
#include<bits/stdc++.h>
using namespace std;
const int N = 10005;
int n,m,s;
string tmp;
vector<char> G[N];
stack<char> stk;
string ans;
int main(){
ios::sync_with_stdio;
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s;
for(int i = 1; i <= n; i++){
cin>>tmp;
for(int j = 0; j < tmp.size(); j++){
//m?
G[i].push_back(tmp[j]);
}
}
int ope;
while(1){
cin>>ope;
//结束条件
if(ope == -1) break;
//无效操作
else if(ope > n) continue;
else if(ope == 0){
if(stk.empty()) continue;
//筐空时不执行任何操作
ans += stk.top();
//!
stk.pop();
}
else{
//筐满 轨道非空
if(stk.size() == s && G[ope].empty() != 1){
ans += stk.top();
stk.pop();
stk.push(G[ope][0]);
G[ope].erase(G[ope].begin(),G[ope].begin() + 1);
}
else{
//筐未满
//轨道非空(轨道空时不执行任何操作
if(G[ope].empty() != 1){
stk.push(G[ope][0]);
G[ope].erase(G[ope].begin(),G[ope].begin() + 1);
}
}
}
}
cout<<ans<<endl;
return 0;
}
思路:看似复杂的模拟题。
7 排队
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n,m,cnt[N],f[N],rk[N];
bool flag1,flag2;
/*
int root(int a){
while(a != f[a]){
f[a] = f[f[a]];
a = f[a];
}
return a;
}
*/
void initialize(){
for(int i = 1; i <= n; i++){
f[i] = i;
}
}
int root(int a){
return a == f[a] ? a : (f[a] = root(f[a]));
}
void Union(int a, int b){
int ra = root(a);
int rb = root(b);
//优化:高树的根成为矮树的根
if(rk[ra] > rk[rb]){
f[rb] = ra;
}
else if(rk[rb] > rk[ra]){
f[ra] = rb;
}
else{
rk[ra]++;
f[rb] = ra;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
initialize();
for(int i = 1; i <= m; i++){
int x, y;
cin>>x>>y;
cnt[x]++, cnt[y]++;
//结点的度增加
//判断:根是否相同(是否形成回环)
if(root(x) == root(y)){
flag1 = true;
}
Union(x,y);
}
for(int i = 1; i <= n; i++){
//结点度数要小于等于2
if(cnt[i] > 2){
flag2 = true;
break;
}
}
if(flag1 || flag2)
cout<<"No\n";
else
cout<<"Yes\n";
return 0;
}
思路:解决一个是相邻的问题(一个数只能和不超过两个数相邻,即结点的度小于等于2),一个是形成回环的问题(要求是排成一排)。前者通过累计度数判断是否超过2解决,后者通过并查集判断两个结点的根是否在合并时已经相同解决。
8 整齐的数组
#include<bits/stdc++.h>
using namespace std;
const int N = 50;
int a[N], b[N], k, t, n, tmp, a_min;
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
memset(a, 0 , sizeof(a));
a_min = 0x3f3f3f;
tmp = 0;
cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i];
a_min = a_min > a[i] ? a[i] : a_min;
}
for(int i = 1; i <= n; i++){
if(a[i] == a_min) continue;
b[++tmp] = a[i] - a_min;
}
// for(int i = 1; i <= tmp; i++){
// cout<<b[i]<<" ";
// }
// cout<<endl;
if(tmp == 0){
cout<<"-1\n";
}
else if(tmp == 1){
cout<<b[1]<<endl;
}
else{
for(int i = 2; i <= tmp; i++){
b[i] = gcd(b[i-1], b[i]);
}
cout<<b[tmp]<<endl;
}
}
return 0;
}
/*
int gcd(int a, int b){
int c;
while(b > 0){
c = a % b;
a = b;
b = c;
}
return a;
}
*/
思路:先找到数组中最小的数,然后逐项求差(当然是不同的数),然后相邻两项差求最小公倍数(GCD)。另外题意中输出 -1 的情况就是数组中所有数是相同的。
9 01序列
#include<bits/stdc++.h>
using namespace std;
const int N = 1000005;
long long ans, k;
int f[N], cnt[N];
string str;
int main(){
cin >> k >> str;
int sum = 0;
for(int i = 0; i < str.length(); i++){
if(str[i] == '1') sum++;
f[i] = sum;
cnt[f[i]]++;
}
if(k != 0){
for(int i = 0; i < str.length(); i++){
ans += cnt[f[i] + k];
}
cout << cnt[k] + ans << endl;
}
else{
long long cnt = 0;
str += "1";
for(int i = 0; i < str.length(); i++){
if(str[i] == '0') cnt++;
else{
ans += (cnt + 1) * cnt / 2;
cnt = 0;
}
}
cout << ans << endl;
}
return 0;
}
思路:前缀和。注意k等于0时候的情况。
10 锦标赛
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
ll a[N], K, n;
bool cmp(int a, int b){
return a > b;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> K;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1, a + 1 + n, cmp);
ll j;
for(j = 1; j < n; j++){
if(a[j] - a[j + 1] > K){
break;
}
}
cout << j << endl;
return 0;
}
思路:先从大到小排序,只要前项与后项差值大于 K 的之后(包括该后项)所有人都是没有机会拿到冠军的。