AHUACM 第二场摸底测试复盘题解
AHUACM第二场摸底测试复盘题解
A:Bottles(2016-2017 ACM-ICPC, NEERC, Southern Subregional Contest J)
难度:1900
这题只要注意到n=100对应的是\(O(n^4)\)的时空复杂度,就是比较水的dp了。(不过被2分钟就切的yb误导了,以为是脑筋急转弯)
性质:容器瓶子需要装的水量恒定
f[i][j][k]表示在前i个瓶子中,已经选了j个瓶子作为容器,容器总容量为k时,需要往容器倒的水的最小值
#include<bits/stdc++.h>
using namespace std;
int a[105],b[105];
int f[101][101][10001];
int main()
{
int n;
cin>>n;
int sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
for(int k=0;k<=10000;k++)
{
f[i][j][k]=30000;
}
}
}
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
for(int k=0;k<=10000;k++)
{
f[i][j][k]=min(f[i-1][j][k]+a[i],f[i][j][k]);
if(j>=1&&k>=b[i])f[i][j][k]=min(f[i][j][k],f[i-1][j-1][k-b[i]]);
}
}
}
int ans1=0,ans2=30000;
for(int i=1;i<=n;i++)
{
for(int j=sum;j<=10000;j++)
{
if(f[n][i][j]<=j)
{
ans1=i;
break;
}
}
if(ans1)break;
}
for(int j=sum;j<=10000;j++)
{
if(f[n][ans1][j]<=j)
{
ans2=min(f[n][ans1][j],ans2);
}
}
cout<<ans1<<" "<<ans2<<endl;
return 0;
}
B:Fish(Codeforces Beta Round 16 E)
难度:1900
一看数据范围就知道是裸地不能再裸的状压dp,状态就是当前还剩的鱼的集合
#include<bits/stdc++.h>
using namespace std;
#define LL long long
double f[20][(1<<18)+10];
double p[20][20];
int t[20];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>p[i][j];
}
}
f[0][(1<<n)-1]=1;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<(1<<n);j++)
{
if(f[i][j]<1e-9) continue;
int m=0;
for(int k=0;k<n;k++)
{
if((j&(1<<k))==0) continue;
t[++m]=k+1;
}
if(i!=n-m)continue;
double cnt=1.0*m*(m-1)/2;
for(int x=1;x<=m;x++)
{
for(int y=x+1;y<=m;y++)
{
f[i+1][j^(1<<(t[y]-1))] += f[i][j] * p[t[x]][t[y]] / cnt;
f[i+1][j^(1<<(t[x]-1))] += f[i][j] * p[t[y]][t[x]] / cnt;
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%.10f ",f[n-1][1<<(i-1)]);
}
return 0;
}
C: Coloring Game(Pinely Round 4 (Div. 1 + Div. 2) E)
难度:1900
这题本质上就是二分图判断。
当给出的图不是二分图时,Alice只需要只给两种颜色,就能自动胜利
当给出的图是二分图时,Bob 必胜法如下:
当二分图两侧点都没用完时,可以忽略颜色3,只用颜色1或颜色2。而当其中一侧的点用完后,就用另一侧点原本对应的颜色或颜色3填另一侧的点。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10005;
vector<int>e[maxn];
int fa[maxn],col[maxn];
bool ck;
void dfs(int u)
{
for(int v:e[u])
{
if(fa[u]==v)continue;
if(col[v]!=-1)
{
if(col[v]==col[u])
{
ck=true;
return;
}
}else
{
col[v]=col[u]^1;
fa[v]=u;
dfs(v);
}
}
}
void solve()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
e[i].clear();
col[i]=-1;
}
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
ck=0;col[1]=0;
dfs(1);
if(ck)
{
cout<<"Alice"<<endl;
for(int i=1;i<=n;i++)
{
cout<<1<<" "<<2<<endl;
int u,c;
cin>>u>>c;
}
}else
{
cout<<"Bob"<<endl;
set<int>c1,c2;
for(int i=1;i<=n;i++)
{
if(col[i]==0)c1.insert(i);
else c2.insert(i);
}
int c3=0;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
if(a>b)swap(a,b);
if(c1.size()&&c2.size())
{
if(a==1)
{
cout<<(*c1.begin())<<' '<<1<<endl;
c1.erase(c1.begin());
}
else
{
cout<<(*c2.begin())<<' '<<2<<endl;
c2.erase(c2.begin());
}
}else if(c1.size())
{
if(a==1)
{
cout<<(*c1.begin())<<' '<<1<<endl;
c1.erase(c1.begin());
}
else
{
cout<<(*c1.begin())<<' '<<3<<endl;
c1.erase(c1.begin());
}
}else if(c2.size())
{
if(a==2||b==2)
{
cout<<(*c2.begin())<<' '<<2<<endl;
c2.erase(c2.begin());
}
else
{
cout<<(*c2.begin())<<' '<<3<<endl;
c2.erase(c2.begin());
}
}
}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
D: Ananna(The 2025 ICPC Latin America Championship A)
bfs,恰好只需把所有点对(i,j)跑一次。
关键性质:一个回文点对(i,j)可以由另外一个回文点对扩展出来。
一开始的思路类似于弗洛伊德,始终不知道怎么O(1)在不同的状态之间进行转移。
只能说在涉及图论的题目时,搜索确实是比递推更具有优越性的。
以后遇到图论题时,首先应该考虑能否由bfs解决
时间复杂度证明:
一个点对(i,j)最多只会进队一次,这部分复杂度不超过\(O(n^2)\)
然后是枚举边的部分:
因此总复杂度为\(O(n^2+m^2)\)
#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long
const int maxn = 5005;
vector<pair<int,char>>e1[maxn],e2[maxn];
bool f[maxn][maxn];
int main()
{
int n,m;
cin>>n>>m;
queue<pair<int,int>>q;
for(int i=1;i<=m;i++)
{
int x,y;
char c;
cin>>x>>y>>c;
e1[x].push_back({y,c});
e2[y].push_back({x,c});
if(!f[x][y])
{
f[x][y]=1;
q.push({x,y});
}
}
for(int i=1;i<=n;i++)
{
q.push({i,i});
}
while(!q.empty())
{
int x=q.front().first;
int y=q.front().second;
q.pop();
for(auto dx:e2[x])
{
for(auto dy:e1[y])
{
if(dx.second==dy.second && !f[dx.first][dy.first])
{
f[dx.first][dy.first]=1;
q.push({dx.first,dy.first});
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(f[i][j]&&i!=j)
{
ans++;
}
}
}
coutans;
return 0;
}
E: Baby Ehab Partitions Again(Codeforces Round 717 C)
难度:1700
可以注意到,想把数列分成和相等的两部分,数列和一定是偶数,很容易想到:删除一个奇数,就能把它变成奇数。
并且,数列中的每个数除以数列gcd队划分不会产生影响,当数列中没有奇数时,这么处理能保证获得至少一个奇数,将其删除即可。
f[i][j]表示能否从前i个数中凑出部分数列和j,f[n][sum/2]就能判断能否分成两部分。
#include<bits/stdc++.h>
using namespace std;
bool f[105][200005];
int a[105],n,sum,cnt;
bool check()
{
if(sum%2==1)return false;
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=sum;j++)
{
.
f[i][j]=f[i-1][j];
if(j>=a[i])
{
f[i][j]|=f[i-1][j-a[i]];
}
}
}
return f[n][sum/2];
}
bool check2()
{
for(int i=1;i<=n;i++)
{
if(a[i]%2!=0)return 0;
}
return 1;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
if(check()==0)
{
cout<<0<<endl;
}else
{
while(check2())
{
cnt++;
for(int i=1;i<=n;i++)
{
a[i]/=2;
}
}
for(int i=1;i<=n;i++)
{
if(a[i]%2)
{
cout<<1<<endl<<i<<endl;
return 0;
}
}
}
return 0;
}
F Isomorphic Strings(Educational Codeforces Round 44 F)
难度:2300
赛时想到哈希,但是不知道怎么哈
一个长度为n的字符串,它所有字符的情况可以由26个长度为n的01串表示
而这个长度为n的01串可以被哈希化为一个整数,因此只需要26个整数就可以表示一个字符串的所有字母的分布情况
判断两个字符串是否是同构的,只需要判断它们的26个整数是否相等即可
因此对于这道题,我们用f[i][c]表示前i个字符中,字符c的分布情况对应的哈希值,就能在\(O(26)\)知道任何一个区间的字符分布情况:
也就是把26个哈希值塞进一个vector并排序,得到的vector就代表了这个区间的字符分布情况
#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long
const LL mod = 1e9+7;
const LL base=131;
const int maxn = 200005;
LL f[maxn][26],bs[maxn];
LL query(int l,int r,int c)
{
if(l==0)return f[r][c];
return (f[r][c]-f[l-1][c]*bs[r-l+1]%mod+mod)%mod;
}
void solve()
{
int n,m;
cin>>n>>m;
string s;
cin>>s;
for(int i=0;i<s.length();i++)
{
for(int j=0;j<26;j++)
{
if(i==0)f[i][j]=s[i]==(j+'a');
else f[i][j]=(f[i-1][j]*base%mod+(s[i]==(j+'a'))) % mod;
}
}
bs[0]=1;
for(int i=1;i<n;i++)
{
bs[i]=bs[i-1]*base%mod;
}
while(m--)
{
int x,y,len;
vector<int>a,b;
cin>>x>>y>> len;
x--; y--;
for(int i=0;i<26;i++)
{
a.push_back(query(x,x+len-1,i));
b.push_back(query(y,y+len-1,i));
}
sort(a.begin(),a.end());
sort(b.begin(),b.end());
if(a == b)
{
cout<<"YES"<<endl;
}
else
{
cout<<"NO"<<endl;
}
}
}
int main()
{
int T=1;
while(T --> 0)
{
solve();
}
return 0;
}
浙公网安备 33010602011771号