CF Round #533 (Div. 2)

博客分享了Codeforces Round #533 (Div. 2)的比赛题目,包括A、B、C、D、E五道题目的详细解析,提供了ACM竞赛题目的暴力解法。
摘要由CSDN通过智能技术生成

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
题目链接:https://codeforces.com/contest/1105

A

有n个数,找到一个t然后把这n个数变到与t的差的绝对值小于等于1
改变一个数的代价为新数-原数的绝对值
问最小的费用是多少
因为数的范围在1到100,而且n比较小 
所以枚举t就行了 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

int n,a[1006];
int mi = 99999999,ans;
int main(){
	scanf("%d",&n);
	fo(i,1,n)scanf("%d",&a[i]);
	fo(t,1,100){
		int nowmi = 0;
		fo(i,1,n){
			if(fabs(t-a[i])>1){
				nowmi += fabs(t-a[i])-1;
			} 
		}	
		if(nowmi<mi){
			mi = nowmi;
			ans = t;
		}	
	}
	cout<<ans<<" "<<mi<<endl;
	return 0;
}
B
给定一个字符串,和数字k
当k个相同的字母连续出现,那么这段字母就是合法的
问最多有多少个相同的合法字母段 字母段之间不能交叉
扫一遍就行.... 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,k,ans[200],mx;
char s[200005];
int main(){
	scanf("%d%d",&n,&k);
	scanf("%s",s+1);
	char lst = '#';
	int sum=0;
	fo(i,1,n){
		if(lst=='#'||s[i]==lst){
			sum++;
			lst=s[i];
		}else{
			sum=1;
			lst = s[i];
		}
		if(sum==k){
			sum=0;
			lst = '.';
			ans[s[i]-'a']++;
			mx = max(mx,ans[s[i]-'a']);
		}
	}
	cout<<mx<<endl;
	return 0;
}
C
题目:有n个数,大小范围在[l,r],n个数之和为3的倍数,问有多少种组成方法
--------------------------------------------------------------
sum%3 = a1%3 + a2%3 + a3%3 + ... + an%3
所以我们只需要关心 mod 3 == 0 or 1 or 2 的数的数量就好
例如: 
x ≡1 mod 3 
则 x = 3k+1
L <= 3k+1 <= R
(L-1)/3 <= k <= (R-l)/3
所以k的数量(也就是x的数量) =
	floor( (R-l)/3) - ceil((L-1)/3)  + 1
余数为0或2同理可求
另dp[i][j]表示第i层余数为j的组成方法数
	那么dp[i][j]可以从第i-1层的状态转移过来
	每层有3个状态,转移的状态也有3个
所以答案为dp[n][0] 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int mod = 1e9+7;
ll n,l,r,dp[200005][3],m[3];
int main(){
	cin>>n>>l>>r;
	// 余数为0的数的个数 
	m[0] = r/3 - (l-1)/3; // 下限减上限的数量 = 下限 -下限(-1就变成下限了) 
	m[1] = (r-1+3)/3 - (l-1+3-1)/3; // 在做减法的时候加上除数(余数)(左右都得加),不然会可能变复数 
	m[2] = (r-2+3)/3 - (l-2+3-1)/3;
	dp[0][0] = 1;
	fo(i,1,n){ // 阶段 
		fo(j,0,2){ // 状态 
			fo(k,0,2){ // 转移状态 
				// dp[i][j]表示第i层余数为j的数量 
				dp[i][(j+k)%3] = (dp[i][(j+k)%3] + dp[i-1][j]*m[k]%mod) % mod;
			}
		}
	} 
	cout<<dp[n][0]<<endl;
	return 0;
}
D
有p个玩家在一个网格上,
初始的时候网格上有玩家的初始城堡(可能多个,但是每个网格只能有一个城堡) 
'.'表示空,'#'表示障碍物
每个玩家有一个扩展速度,表示他在每轮扩展的时候只能行走speed[i]步
当所有玩家无法继续扩展的时候游戏结束
输出玩家最终最终占领城堡的数量
----------------------
解法:妈呀,自己写的一份代码第45个样例时间超了?找不出bug...
以下是AC代码 
轮流对p个玩家进行bfs,每次每个玩家扩展speed[i]层...
当扩展到speed[i]层的时候退出bfs
如果某个玩家的列队为空则表示他已经无法进行bfs扩展了 
#include<bits/stdc++.h>
#define ll long long
#define mk make_pair
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int dx[] = {-1,1,0,0};
const int dy[] = {0,0,-1,1};
int n,m,p,ans[10];
char mp[1005][1005];
int dis[1005][1005];
ll speed[10]; 
queue< pair<int,int> > q[10];
bool ok(int x, int y){
	if(1<=x&&x<=n && 1<=y&&y<=m &&mp[x][y]=='.')return 1;
	else return 0;
}
// 第i个玩家,第k轮扩展 
void bfs(int i,int k){
	ll nowSpeed = k*speed[i];
	while(!q[i].empty()){
		int x = q[i].front().first;
		int y = q[i].front().second;
		// 断层,不能多走了,接下来的在下一轮 
		if(dis[x][y]==nowSpeed)break;
		q[i].pop();
		fo(j,0,3){
			int nowx = x+dx[j];
			int nowy = y+dy[j];
			if(ok(nowx,nowy)){
				dis[nowx][nowy] = dis[x][y]+1;
				mp[nowx][nowy] = mp[x][y];
				ans[mp[x][y]-'0']++;
				q[i].push(mk(nowx,nowy));
			}
		}
	}
}
void solve(){
	int k = 0;
	while(1){
		int no = 0; // 无法继续扩展的玩家数量 
		k++; // 第k轮 
		fo(i,1,p){ // 依次遍历P个玩家 
			bfs(i,k);
			if(q[i].empty())++no; // 该玩家已经无法扩展 
		}
		if(no==p)break; // 所有玩家无法扩展游戏结束 
	}
	fo(i,1,p){
		printf("%d%c",ans[i],i==p?'\n':' ');
	}
}
int main(){
	cin>>n>>m>>p;
	fo(i,1,p)cin>>speed[i];
	fo(i,1,n){
		getchar(); // 末尾换行符. 
		fo(j,1,m){
			scanf("%c",&mp[i][j]);
		//	printf("test:%c\n",mp[i][j]);
			if('1'<=mp[i][j]&&mp[i][j]<='9'){
				q[mp[i][j]-'0'].push(mk(i,j));
				ans[mp[i][j]-'0']++;
			}
		}
	}
	solve();
	return 0;
}
E
你有一个主页,主页上有一个名字,现在有两种操作
1      你可以修改主页上的名字
2  S   名字为S的同学来看你的主页
1
2  S1
2  S2
2  S3
.... 
如果S看到主页上的名字为他的名字,那么他就会开心
先在有 N 个这样的操作,且最多有40种S
问到最后一直开心的同学最多有多少个
--------------------------------
暴力不满分做法
对两个1之间的数据建立一个层
层上的同学为 S1 S2 S3 S4...
显然,最多只能让一个同学开心
枚举让哪个同学开心(该同学还开心着,不开心的剪枝)
用2进制1表示某位同学还开心着 
然后逐层搜索....
加上剪纸和记忆化...时间为 2^40*40 吧?
--------------------------------
正解:
把所有同在过一个层的建立一条边
G[i]表示与第i个同学同在一个层上过(包扩i同学)
然后对已有的集合Set
取出Set里的同学u,考虑要不要选u同学
选: Set里去掉G[u],则该状态的答案加1(凡出现u同学的都选)  
不选: Set里去掉u 
这样最多考虑 40个同学,时间复杂的为O(2^40)
考虑记忆化到可以优化到O(2^20)
为什么呢?考虑最坏的情况,
每次选择一个u时,我们要去掉2个同学(包括u同学)
那么选则集合少两个
不选则集合少一个,下次继续不选则会变成集合少两个的情况(偶数的计算过了)
那么到达空集的时候最多需要递归 m/2 层

你可能会问,去每次去掉一个同学不是更好?
这种情况下选或者不选都是去掉相同的同学
那么分出来的集合Set1和Set2 是一样的,那么则不需要分支递归
因为另一个分支已经计算过了 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 100005;
int n,m,k,ed;
unordered_map<string,int> mp;
ll ceng[maxn],G[50]; // 边~ 
inline string read()//inline继续加快速度
{
	char ch=getchar();
	string st1="";
	while (!((ch>='a')&&(ch<='z')))//把前面没用的东西去掉,当然,ch在什么范围内可以依据需要修改 
	  ch=getchar();
	while ((ch>='a')&&(ch<='z')) 
	  st1+=ch,ch=getchar();
	return st1;//返回
}
unordered_map<ll,int> dp={{0ll,0}}; // 初始化 
ll dfs(ll mask){
	if(dp.count(mask))return dp[mask];
	int u = __builtin_ctzll(mask);// 最后一位非0位 
	return dp[mask]=max(dfs(mask^(1ll<<u)),1+dfs(mask&G[u]));
	// 选了u那么与u同一层过的都要去掉
	// 不选u则去掉u 
}
int main(){
	scanf("%d%d",&n,&m);
	int opt,flag=1;
	string s;
	fo(i,1,n){
		scanf("%d",&opt);
		if(opt==1&&flag){
			flag=0;
			ed++;
		}else if(opt==2){
			flag=1;
			s = read();
			if(!mp.count(s))mp[s]=k++;
			ceng[ed] |= 1ll<<mp[s]; // 同一层的数标记为1 
		}
	}
	// 建图 
	fo(c,1,ed){ // 层 
		for(int i=0; i<k; i++){
			for(int j=0; j<k; j++){
				if((ceng[c]>>i)&1 && (ceng[c]>>j)&1){ // i,j同属过一个层 
					G[i] |= 1ll<<j;
				}
			}
		}
	}
	// 取反后使用 &运算 就可以直接去掉与i同层的数了
	for(int i=0; i<k; i++)G[i] = ~G[i]; 
	// 输出图								    
//	fo(x,0,k-1){									
//		for(int i=m-1; i>=0; i--){
//			if((G[x]>>i)&1)cout<<1;
//			else cout<<0;
//		}
//		cout<<endl;	
//	}
	cout<<dfs((1ll<<m)-1); 
} 

以下是暴力解法

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 100005;
int n,m,name[50],ed,ans,k;
ll ceng[100005];
vector<int>G[maxn]; 
unordered_map<string,int>mp;

inline string read()//inline继续加快速度
{
	char ch=getchar();
	string st1="";
	while (!((ch>='a')&&(ch<='z')))//把前面没用的东西去掉,当然,ch在什么范围内可以依据需要修改 
	  ch=getchar();
	while ((ch>='a')&&(ch<='z')) 
	  st1+=ch,ch=getchar();
	return st1;//返回
}
// 过了9组 
inline void dfs(int u, ll now){
	if(u==ed+1){
		int t=0;
		for(register int i=0;i<k;i++){
			if((now>>i)&1)t++;
		} 
		ans = max(ans,t);
		return;
	}
	if(now==0)return;
	bool flag = 1; 
	for(register int v:G[u]){
		if((now>>v)&1){
			flag=0;
			dfs(u+1,now&(ceng[u]|(1ll<<v)));
		}
	}
	if(flag)dfs(u+1,now&ceng[u]);
}
// 过了10组 
unordered_map<ll,int>dp;
inline int dfs2(int u, ll now){
	if(dp.count(now))return dp[now];
	if(u==ed+1){
		int t=0;
		for(register int i=0;i<k;i++){
			if((now>>i)&1)t++;
		} 
		return dp[now]=t;
	}
	if(now==0)return dp[0ll]=0;
	bool flag = 1;
	int t = 0; 
	for(register int v:G[u]){
		if((now>>v)&1){
			flag=0;
			t = max(t,dfs2(u+1,now&(ceng[u]|(1ll<<v))) );
		}
	}
	if(flag)t=max(t , dfs2(u+1,now&ceng[u]) );
	return dp[now]=t;
}
int main(){
	cin>>n>>m;
	fo(i,1,n+1){
		ceng[i] = (1ll<<m)-1;
	}
	int typ;
	string s;
	bool flag=1;
	fo(i,1,n){
		scanf("%d",&typ);
		if(typ==1&&flag){
	//		for(int i=m; i>0; i--){
//				if((ceng[ed]>>i)&1)cout<<1;
//				else cout<<0;
//			}
//			cout<<endl; 
			flag=0;
			ed++;
			memset(name,0,sizeof(name));
		}else if(typ==2){
			flag=1;
		//	cin>>s;
			s=read();
			if(!mp[s])mp[s]=++k;
			if(!name[mp[s]-1]){
				name[mp[s]-1]=1;
				G[ed].push_back(mp[s]-1);
				ceng[ed] &= ~(1ll<<(mp[s]-1));//取0,把某一层出现过的数都取0 
			}
		}
	}
	fo(i,1,ed){
		sort(G[i].begin(),G[i].end());
	}
//	dfs(1,(1ll<<m)-1);
//	cout<<ans<<endl;
	dfs2(1,(1ll<<m)-1);
	cout<<dp[(1ll<<m)-1]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值