第二次CCF计算机软件能力认证

做题感受:
410 / 500
前三道题目都比较简单,很快就能写完;T4 没想到超级源点,写了一个朴素BFS拿了90分;T5 不是很会,骗分拿了20分。总的来说T3相对较水,所以做的很顺。

一些需要注意的点:
1、能使用结构体或者数组,就尽量不要使用vector
2、当读入的数据量超过 1e6 就要考虑 使用scanf/printf 而不是 cin/cout

T4因为使用cin在AcWing上一直TLE
T5一开始使用vector写矩阵快速幂(我存的板子),结果导致TLE;并且T4在存储询问数据的结构选择上,使用结构体存储比使用vector<Node>存要快 50ms 左右

主要收获:

题目编号题目名称知识点
T4201403-4 无线网络多源最短路问题
T5201403-5 任务调度状压DP + 矩阵快速幂

201409-1 相邻数对

水题,排个序统计一下就能得到答案:

#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn=1e4+5;

int n,x,a[maxn],ans; 

int main()
{
	for(cin>>n;n;n--){
		cin>>x;
		a[x]++;
	}
	for(int i=0;i<10000;i++)
		if(a[i]&&a[i+1]) ans++;
	cout<<ans<<'\n';
	return 0;
}

201409-2 画图

数据范围比较小,所以也是简单题,直接暴力模拟涂色即可,复杂度 O ( n 3 ) O(n^3) O(n3)
代码:

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn=105;

int n,a[maxn][maxn],x1,y1,x2,y2,ans;

int main()
{
	cin>>n;
	while(n--){
		cin>>x1>>y1>>x2>>y2;
		for(int i=x1+1;i<=x2;i++)
			for(int j=y1+1;j<=y2;j++)
				a[i][j]++;
	}
	for(int i=1;i<maxn;i++)
		for(int j=1;j<maxn;j++)
			if(a[i][j]) ans++;
	cout<<ans<<'\n';
	return 0;
}

如果题目的数据范围加强的话 就变成一道 T5 了 ,需要用到 线段树 扫描线 来做,复杂度可以降为 n log ⁡ ( n ) n\log(n) nlog(n)

201409-3 字符串匹配

数据范围小,简单题。这里通过 string中自带的find()函数来查找模式串。
若没找到则返回 npos (通过判断是不是 -1 好像也可)

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;

bool search(string pa,string s,int ops){
	if(!ops){
		for(int i=0;i<s.size();i++) 
			if(s[i]<'a') s[i]=s[i]+32;
		for(int i=0;i<pa.size();i++) 
			if(pa[i]<'a') pa[i]=pa[i]+32;		
	}
	return s.find(pa)!=s.npos;
} 

int main()
{
	string s,pattern;
	int ops,n;
	cin>>pattern>>ops>>n;
	while(n--){
		cin>>s;
		if(search(pattern,s,ops)) 
			cout<<s<<'\n';
	}
	return 0;
}

补充说几点:

  • find() 函数是通过暴力查找匹配,但是在一般题目(不专门考察字符串匹配,样例不卡算法)中效率很高。

  • strstr()函数可以进行字符数组的匹配,闫老师说和KMP效率一样,但是查了很多资料后持怀疑态度,没有真正拿题目试过,也没有专门测试过二者的效率。

附:strstr() 解析1 \quad strstr() 解析2

201409-4 最优配餐

读完题目稍加分析,就可以发现这是一道多源最短路问题(注意和我们通常使用floyd求解的多源汇最短路问题进行区分)。

解决这类问题的一个常见技巧是建立一个虚拟的超级源点,然后建立超级源点到每个起点的虚拟边,并令每条边的权值为0。最后在这个新的图上求解单源最短路即可得到答案。

这道题目由于每条边的权重都是1,因而退化为使用BFS求解;并且可以不用真的建立虚拟源点,而是直接将所有源点直接插入队列,这样代码更加简洁。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1e3+5;

bool obs[maxn][maxn],vis[maxn][maxn];
int n,m,k,d;
int dis[maxn][maxn];
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};

struct Node{ int x,y,w; };

void BFS(int sx,int sy){
	memset(vis,0,sizeof vis);
	queue<PII> q;
	q.push(PII(sx,sy));
	dis[sx][sy]=0;
	while(!q.empty()){
		auto u=q.front();
		q.pop();
		if(vis[u.first][u.second]) continue;
		vis[u.first][u.second]=1;
		for(int i=0;i<4;i++){
			int xx=u.first+dx[i], yy=u.second+dy[i];
			if(xx<1||xx>n||yy<1||yy>n||obs[xx][yy]) continue;
			if(dis[xx][yy]>dis[u.first][u.second]+1){
				dis[xx][yy]=dis[u.first][u.second]+1;
				q.push(PII(xx,yy));
			}			
		}
	}
	return;
}

int main()
{
	cin>>n>>m>>k>>d;
	vector<PII> src;
	vector<Node> dst;
	int x,y,w;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		src.push_back(PII(x,y));
	}
	for(int i=1;i<=k;i++){
		cin>>x>>y>>w;
		dst.push_back(Node{x,y,w});
	}
	while(d--){
		cin>>x>>y;
		obs[x][y]=true;
	}
	memset(dis,0x3f,sizeof dis);
	for(auto u:src) BFS(u.first,u.second);
	ll res=0;
	for(auto u:dst){
		res+=(ll)dis[u.x][u.y]*u.w;
	}
	cout<<res<<'\n';
	return 0;
}

这里附一道很久之前写过的,通过建立超级源点,将问题转化为最小生成树的思维好题: Codeforces 1245D

201409-5 拼图

这是一道糅合了 状态压缩DP题、快速幂、矩阵乘法 共3个知识点的题目。
f [ i ] [ j ] f[i][j] f[i][j] 表示放完前 i i i 行,且前 i i i 行已被占用的格子的状态为 j j j 的所有方案数,这里 0 < i ≤ n 0<i\leq n 0<in 0 ≤ j < 2 m 0\leq j < 2^m 0j<2m.

这样状态转移可以用DFS,求出当前行的每个状态到填满该行后下一行各个状态的方案数,存入矩阵 c c c 中,这样从 f [ i ] → f [ i + 1 ] f[i] \rightarrow f[i + 1] f[i]f[i+1] 就相当于乘了一个以方案数为权值的矩阵 c c c.

所以 f [ n ] = f [ 0 ] × c n f[n] = f[0] \times c^n f[n]=f[0]×cn,又因为 f [ 0 ] f[0] f[0]只有 f [ 0 ] [ ( 1 << m ) − 1 ] f[0][(1\texttt{<<}m)-1] f[0][(1<<m)1]是1,其余值都为0,所以直接输出 c n [ ( 1 << m ) − 1 ] [ ( 1 << m ) − 1 ] c^n[(1\texttt{<<}m)-1][(1\texttt{<<}m)-1] cn[(1<<m)1][(1<<m)1]就是答案,当然这里数据范围很大,需要通过快速幂优化。

时间复杂度: O ( ( 2 m ) 3 log ⁡ n ) O((2^m)^3\log n) O((2m)3logn)

代码:

#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=128+5;

ll n;
int m,c[maxn][maxn];

void mul(int c[][maxn], int a[][maxn], int b[][maxn]){
    static int tmp[maxn][maxn];
    memset(tmp, 0, sizeof tmp);
    for (int i = 0; i < 1 << m; i ++ )
        for (int j = 0; j < 1 << m; j ++ )
            for (int k = 0; k < 1 << m; k ++ )
                tmp[i][j] = (tmp[i][j] + (ll)a[i][k] * b[k][j]) % mod;
    memcpy(c, tmp, sizeof tmp);
}

//当前行状态是cur 下一行状态是nxt 从第s位开始搜 
void DFS(int cur,int nxt,int s){
	if(s==m) c[cur][nxt]++;
	else if(cur>>s&1) DFS(cur,nxt,s+1); 
	else{
		if(s && !(nxt>>s&1) && !(nxt>>s-1&1))
			DFS(cur, nxt+(1<<s)+(1<<s-1), s+1); 
		if(s<m-1 && !(nxt>>s&1) && !(nxt>>s+1&1))
			DFS(cur, nxt+(1<<s)+(1<<s+1), s+1);
		if(s<m-1 && !(cur>>s+1&1)){
			if(!(nxt>>s&1)) DFS(cur, nxt+(1<<s), s+2);
			if(!(nxt>>s+1&1)) DFS(cur, nxt+(1<<s+1), s+2); 
		}
	}
}

int main()
{
	cin>>n>>m;
	for(int i=0;i<(1<<m);i++)
		DFS(i,0,0);
	
	int res[maxn][maxn]={0};
	for(int i=0;i<1<<m;i++) res[i][i]=1;
	while(n>0){
		if(n&1) mul(res,res,c);
		mul(c,c,c);
		n>>=1;
	}
	cout<<res[(1<<m)-1][(1<<m)-1]<<'\n';
	return 0;
}

附两道类似的状压DP题目以便练习:
蒙德里安的梦想
最短Hamilton路径
另附 快速幂板子

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值