T1 跳石头
二分即可
#include<bits/stdc++.h> using namespace std; int n,m,d,l,r,mid,ans,a[100000]; bool judge(int x){ int tot=0,i=0,now=0; while (i<n+1) { i++; if(a[i]-a[now]<x) tot++; else now=i; } return tot<=m; } int main(){ freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); cin>>d>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; a[n+1]=d,l=1,r=d; while(l<=r){ mid=(l+r)/2; if (judge(mid)){ ans=mid; l=mid+1; } else r=mid-1; } cout<<ans; return 0; }
T2 子串
DP。我觉得和背包的思路挺像的一道题。用f1[i][j][k]表示两串分别到a[i],b[j],且a[i]必须使用时,用了k个子串的方案数。f2[i][j][k]表示无论用不用当前字符(A[i])时的方案数总和。那么转移方程就显而易见了:
f1[i][j][k]=f1[i-1][j-1][k-1]+f2[i-1][j-1][k]
f2[i][j][k]=f2[i][j][k]+f[i][j][k]
我们发现i这个维度是可以滚动的,于是就节省了一维空间
#include<bits/stdc++.h> using namespace std; int mod=1000000007; char a[1001],b[201]; int n,m,k1,y,ans,f1[1001][200],f2[201][201]; int main(){ //freopen("substring.in","r",stdin); //freopen("substring.out","w",stdout); cin>>n>>m>>k1; cin>>a>>b; f1[0][0]=f2[0][0]=1; for (int i=1; i<=n; i++) for (int j=m; j>=1; j--) for (int k=1; k<=k1; k++) { if (a[i-1]!=b[j-1]) {f1[j][k]=0; continue;} f1[j][k]=(f1[j-1][k]%mod+f2[j-1][k-1]%mod)%mod; f2[j][k]=(f2[j][k]%mod+f1[j][k]%mod)%mod; } printf("%d",f2[m][k1]); return 0; }
T3 运输计划
先Tarjan求m对点的lca,然后二分答案,把所有大于二分的答案的路径记录,判断有没有一条边被所有大于二分的答案的路径所重复,如果有,那么这条边必须大于等于最大的路径减二分的答案,如果上述两个条件都符合那么这个二分的答案就是合法的,细节参考代码。如果TLE加个快读即可。
#include<cstdio> #include<vector> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; typedef pair<int,int>PP; struct node{ int to,dist,nu; }; struct P{ int a,b,c; }; int q[300002],n,m,hh[300002],ans[300002],anss[300002],lca[300002],h[300002],fa[300002],left,right,mid,f[300002]; P y[300002]; bool fl[300002]; vector<node>s[300002]; vector<PP>t[300002]; int find(int x){ if (fa[x]==x)return x; else return fa[x]=find(fa[x]); } void dfs(int x,int tt){ anss[x]=tt;fa[x]=x; for (int i=0;i<s[x].size();i++) if (!fa[s[x][i].to]) { h[s[x][i].to]=s[x][i].nu; dfs(s[x][i].to,tt+s[x][i].dist); fa[s[x][i].to]=find(x); } for (int i=0;i<t[x].size();i++) if (fa[t[x][i].first])lca[t[x][i].second]=find(t[x][i].first); } void dfs(int x){//c++支持重载函数 for (int i=0;i<s[x].size();i++) if (!fl[s[x][i].to]) { fl[s[x][i].to]=1; dfs(s[x][i].to); f[h[x]]+=f[h[s[x][i].to]]; } } bool pd(int x){ int t=0,u,maxn=0; memset(f,0,sizeof(f)); memset(fl,0,sizeof(fl)); for (int i=0;i<m;i++) if (ans[i]>x){maxn=max(ans[i],maxn);q[t++]=i;} if (!t)return true; for (int i=0;i<t;i++) { u=q[i]; f[h[y[u].a]]++;//这条边+1 f[h[y[u].b]]++;//这条边+1 f[h[lca[u]]]-=2;//这条边-2,因为这条边虽然计算了两次,实际上一次都没有走到 } dfs(1); for (int i=1;i<n;i++) if (f[i]==t && hh[i]>=maxn-x)return true; return false; } int main() { scanf("%d%d",&n,&m); int u,v,w,sum=0; for (int i=1;i<n;i++) { scanf("%d%d%d",&u,&v,&w);sum+=w; hh[i]=w;node g; g.to=v;g.dist=w;g.nu=i; s[u].push_back(g); g.to=u; s[v].push_back(g); } for (int i=0;i<m;i++) { scanf("%d%d",&y[i].a,&y[i].b); t[y[i].a].push_back(PP(y[i].b,i)); t[y[i].b].push_back(PP(y[i].a,i)); } dfs(1,0); for (int i=0;i<m;i++) ans[i]=anss[y[i].a]+anss[y[i].b]-2*anss[lca[i]];//a到b的距离等于1到a的距离+1到b的距离-1到lca(a,b)的距离 left=0;right=sum; while(left<right)//二分答案 { mid=(left+right)/2; if (pd(mid))right=mid; else left=mid+1; } printf("%d",right); return 0; }