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;
}