WEEK3

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 的之后(包括该后项)所有人都是没有机会拿到冠军的。 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值