桂林电子科技大学第三届ACM程序设计竞赛

很久没有写题解了。

今年桂电比赛又开始了,本来想比初赛的结果在高铁上就没有写。拖了两天来补个题解吧。

(题解顺序是解题顺序)

H题 分离

https://ac.nowcoder.com/acm/contest/558/H

签到题,纯粹模拟,没什么难度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
int main(){
	int n;
	string s1;
	cin>>n;
	while(n--){
		cin>>s1;
		for(int i = 0 ; i < s1.length() ; i+=2)
			cout<<s1[i];
		cout<<endl;
	}
	return 0; 
} 

B题 重复

https://ac.nowcoder.com/acm/contest/558/B

会map容器的,应该一分钟。不会的可以用的方法也有很多,比如转成哈希表,字符串最小表达(我不知道这个会不会TLE)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
map<string,bool> mp;
int main(){
	int n,m;
	cin>>n>>m;
	string s;
	while(n--){
		cin>>s;
		mp[s] = 0;
	}
	cout<<mp.size()<<endl;
	return 0; 
} 

I题 选择

https://ac.nowcoder.com/acm/contest/558/I

这是一道很明显的DP题。选取ai后不能选ai+1。

所以每个位置有两个状态,假设0:不选该数,1:选取该数

那么DP方程就显而易见dp[i][0] = max(dp[i-1][1],dp[i-1][0])

                                          dp[i][1] = max(dp[i-1][0]+ai,dp[i-1][1])

然后遍历一遍就OK了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 105;
int a[_max];
int dp[_max][2];
int main(){
	int T,n;
	cin>>T;
	while(T--){
		cin>>n;
		memset(dp,sizeof(dp),0);
		for(int i = 1 ; i <= n ; i++) cin>>a[i];
		for(int i = 1 ; i <= n ; i++){
			dp[i][0] = max(dp[i-1][1],dp[i-1][0]);
			dp[i][1] = max(dp[i-1][0]+a[i],dp[i-1][1]);
		}
		cout<<(dp[n][0],dp[n][1])<<endl;
	}
	return 0;
}

J题 相聚

这题和去年蓝桥杯的海岛题目很像。BFS搜索,vis做记号就行了。很久没写题目了,很傻逼的最开始mp开的int弄得我一直在想怎么输入不了。。。傻逼了。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 55;
char mp[_max][_max];
bool vis[_max][_max];
int n,m;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
struct node{
	int x,y;
	void init(int x,int y){
		this->x = x;
		this->y = y;
	}
};
bool IsOut(node n1){
	if(n1.x > n || n1.x < 1) return false;
	if(n1.y > m || n1.y < 1) return false;
	if(vis[n1.x][n1.y]) return false;
	return true;
}
void bfs(int x,int y){
	queue<node> q;
	while(!q.empty()) q.pop();
	node now,next;
	now.init(x,y);
	q.push(now);
	vis[now.x][now.y] = 1;
	while(!q.empty()){
		now = q.front();
		q.pop();
		for(int i = 0 ; i < 4 ; i++){
			next.init(now.x+dx[i],now.y+dy[i]);
			if(!IsOut(next)) continue;
			vis[next.x][next.y] = 1;
			q.push(next);
		}
	}
	return ;
}
int main(){
	int T,cnt;
	cin>>T;
	while(T--){
		memset(vis,false,sizeof(vis));
		cnt = 0;
		cin>>n>>m;	
		for(int i = 1 ; i <= n ; i++)
			for(int j = 1 ; j <= m ; j++){
				cin>>mp[i][j];	
				if(mp[i][j] == '0') vis[i][j] = 1;
			}
		for(int i = 1 ; i <= n ; i++) 
			for(int j = 1 ; j <= m ; j++){
				if(vis[i][j]) continue;			
				cnt++;
				bfs(i,j);
			}
		cout<<cnt<<endl;		
	} 
	return 0;
}

E题 区间

这个题目有人说他用上升子序列做TLE了。这里说一下平常的DP中求得最长上升子序列和这道题是不一样的。最长上升子序列要求的不是一个连续的子序列。这里明显是了。这也是一道模拟题,自己理清一下思路,也很简单的。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1005;
int a[_max];
int main(){
	int T,n;
	cin>>T;
	while(T--){
		cin>>n;
		for(int i = 1 ; i <= n ; i++) cin>>a[i];
		int l = 1,r = 1;
		int l_ans = 1,r_ans = 1,len = 0;
		for(int i = 2 ; i <= n ; i++){
			if(a[i] >= a[i-1]) r = i;
			else{
				if(len < r-l){
					l_ans = l;
					r_ans = r;
					len = r-l;
				}
				l = i;
			}
		}
		if(len < r-l){
			l_ans = l;
			r_ans = r;
		} 
		cout<<l_ans<<' '<<r_ans<<endl;
	}
	
	return 0;
}

A题 串串

这是一道模板题,如果不知道后缀数组的自然很难做,知道了就是一个标准模板题,所以直接放代码就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;

char ch[MAXN], All[MAXN];
int SA[MAXN], Rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m; 
char str[MAXN];
//Rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] Rank的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串
void RSort() {
    for (int i = 0; i <= m; i ++) tax[i] = 0;
    for (int i = 1; i <= n; i ++) tax[Rank[tp[i]]] ++;
    for (int i = 1; i <= m; i ++) tax[i] += tax[i-1];
    for (int i = n; i >= 1; i --) SA[tax[Rank[tp[i]]] --] = tp[i]; 
} 

int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; } 

void Suffix() {
    //SA
    for (int i = 1; i <= n; i ++) Rank[i] = a[i], tp[i] = i;
    m = 127 ,RSort(); 
    for (int w = 1, p = 1, i; p < n; w += w, m = p) { 
        for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i; 
        for (i = 1; i <= n; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;
        RSort(), swap(Rank, tp), Rank[SA[1]] = p = 1;
        for (i = 2; i <= n; i ++) Rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n; Height[Rank[i ++]] = k) 
        for( k = k ? k - 1 : k, j = SA[Rank[i] - 1]; a[i + k] == a[j + k]; ++ k);
}

void Init() {
    scanf("%s", str);
    n = strlen(str);
    for (int i = 0; i < n; i ++) a[i + 1] = str[i];
}

int main() {
    Init();
    Suffix();
    ll ans = 0;
    for(int i = 1 ; i <= n ; i++) ans += n-SA[i]+1-Height[i];
    printf("%lld\n",ans);
	return 0;  
}

C题 二元

这题其实我很蠢,首先我是想二分答案来做,然后太久没写代码了。二分想了会,尴尬,然后TLE了。然后重新思考了下数据范围觉得二分答案确实不可行,然后就觉得那肯定是模拟了。就先排序了一边,想着用a为规则从大到小排序,然后前k个全选就有了一个可选内最大的a,然后根据最后要求的a+b最大值的条件来替代已选的元素。那就肯定要队列了。然后因为替换的是最小的就是优先队列了。既然一开始排序是a规则,那优先队列就是b了。想通了就OK了。

#include<bits/stdc++.h>
#define inf 1e9+7; 
using namespace std;
typedef long long ll;
const int _max = 1e5+5;
struct node{
	int a,b;
	bool operator < (const node &n1)const{
		return b > n1.b;
	} 
}n[_max];
bool cmp(node n1,node n2){
	return n1.a > n2.a;
}
int main(){
	priority_queue<node> q;
	int N,k;
	cin>>N>>k;
	for(int i = 1 ; i <= N ; i++) cin>>n[i].a>>n[i].b;
	sort(n+1,n+N+1,cmp);
	for(int i = 1 ; i <= k ; i++) q.push(n[i]);
	int ans = n[k].a + q.top().b;
	for(int i = k+1 ; i <= N ; i++){
		q.pop();
		q.push(n[i]);
		if(ans < n[i].a+q.top().b){
			ans = n[i].a+q.top().b;
		}
	}
	cout<<ans<<endl;
	return 0;
}

F题 点对

这道题目,首先我骂自己是个憨憨,改BUG改了二十分钟,全是莫名其妙的错误,怕不是到了饭点想吃饭了?

分析一下题目,找对点,有对点代表有环,这个很明显的。一个n个节点的环,那么他的对点有n*(n-1)/2对,这个应该挺容易推的就不推了。所以我们要做的就是首先建图,然后把非环的点排除掉。这个地方既然想到了环,自然而然就肯定要想到拓扑,然后我们首先预处理这个图,然后非环的节点排除,然后我们要做的就是把环与环之间分开来。每个环的权值是n*(n-1)/2。然后最后加起来就OK了。

#include<bits/stdc++.h>
#define inf 1e9+7; 
using namespace std;
typedef long long ll;
const int _max = 305;
vector<int> mp[_max];
vector<int> mp1[_max];
int in_degree[_max],out_degree[_max];
int vis_p[_max];
bool vis_mp[_max][_max];
int dfs(int s,int cnt){
	vis_p[s] = 2;
	for(int i = 0 ; i < mp[s].size() ; i++){
		int e = mp[s][i];
		if(vis_p[e] == 1 || vis_mp[s][e]) continue;
		vis_mp[s][e] = 1;
		cnt += dfs(e,vis_p[e] == 0);
	}
	return cnt;
} 
int main(){
	int n,m,u,v;
	cin>>n>>m;
	memset(in_degree,0,sizeof(in_degree));
	memset(out_degree,0,sizeof(out_degree));
	memset(vis_p,0,sizeof(vis_p));
	memset(vis_mp,0,sizeof(vis_mp));
	for(int i = 1 ; i <= m ; i++){
		cin>>u>>v;
		mp[u].push_back(v);
		mp1[v].push_back(u);
		in_degree[v]++;
		out_degree[u]++;
	}	
	for(int i = 1 ; i <= n ; i++){
		if(!vis_p[i] && (!in_degree[i] || !out_degree[i])){
			vis_p[i] = 1;
			if(!in_degree[i]){ 
				out_degree[i] = 0; 
				for(int k = 0 ; k < mp[i].size() ; k++){
					int s = mp[i][k];
					in_degree[s]--;
				}
			}
			else{ 
				in_degree[i] = 0;
				for(int k = 0 ; k < mp1[i].size() ; k++){
					int s = mp[i][k];
					out_degree[s]--;
				}
			} 
			i = 0;
		}
	}	
	int ans = 0;
	for(int i = 1 ; i <= n ; i++){
		if(vis_p[i]) continue;
		int ret = dfs(i,1);
		ans += ret*(ret-1)/2;
	}
	cout<<ans<<endl;
	return 0;
}

做到现在大概已经做了将近三个半小时吧,很久没做题真是老了很多。还是要好好写题啊(呵呵,我就说说,毕设不要做了?)。还有两题就明天写了。晚上可能还要看看英语。加油吧。

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

补说一下,我很绝望的查了一下F题,然后看到了一个人的解法。我突然觉得我很傻逼。只有300的数据范围。为什么不暴力呢。对不起,我是个睿智。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值