【题解】牛客算法周周练7 A、C、D、E
前言:快期末了时间越来越少了,补题也不能做那么多了,这次B题只过了40个大概就不写了吧,只写了比较简单的剩余4个题(其实C、D没见过这种题还真的不是那么简单)
A 收集纸片
这是小白月赛里面的题,我打过那场小白月赛,我当时用的方法是旅行商问题的DP,也就是先处理好两点间的距离dis[i][j],然后dp[i][j]表示终点为i时,此时已经经过点的状态为j,(状态因为是二进制数,1表示已经经过,0表示还未经过)花费的距离是多少。
三重循环,第一重循环是:枚举状态,然后枚举起点,最后枚举终点。
dp转移方程为:
因为n只有10,其实用next_permutation枚举所有的情况也可以。
代码
#include<bits/stdc++.h> using namespace std; const int maxn=1<<15; int dp[25][maxn]; int x[25],y[25]; int dis[20][20]; int main(){ int T; scanf("%d",&T); while(T--){ int rx,ry; scanf("%d%d",&rx,&ry); scanf("%d%d",x+0,y+0); int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",x+i);scanf("%d",y+i); } for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ dis[i][j]=dis[j][i]=abs(x[i]-x[j])+abs(y[i]-y[j]); } } int N=1<<(n+1); memset(dp,0x3f,sizeof(dp)); for(int i=1;i<N;i++){ for(int j=0;j<=n;j++){ if(dp[j][i]==0x3f3f3f3f) continue; if((i>>j)&1){ for(int c=1;c<=n;c++){ dp[c][i^(1<<c)]=min(dp[c][i^(1<<c)],dp[j][i]+dis[j][c]); } } } } int ans=0x3f3f3f3f; for(int i=0;i<=n;i++){ ans=min(ans,dp[i][N-1]+dis[i][0]); } printf("The shortest path has length %d\n",ans); } return 0; }
C Rabbit的工作(1)
很明显的DP了,但是这个题有意思的一点是要用滚动数组,不然会MLE。
首先看到n是400,400400400也不到1e8,可以用n^3的暴力。
所以联想到dp数组要开三维,正常来说是这三维:dp[i][j][k]
i表示前i天,j是已经工作了j天,k是到目前已连续工作了多少天。
那么很容易得到转移方程:
如果这天不工作或者这天本来就不用工作:dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][k])
如果今天需要工作:dp[i][j][k]=min(dp[i][j][k],dp[i-1][j-1][k-1]+k)
但是因为会MLE,同时我们发现dp数组只跟dp[i-1]有关,所以我们可以把第一维压到2,通过奇偶来表示不同的上下两层就好了。
代码
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<vector> #include<cstring> #include<queue> #define fs first #define se second #define pb push_back #define cppio ios::sync_with_stdio(false);cin.tie(0) #define eps 1e-7 using namespace std; typedef long long ll; typedef pair<int,int> pii; typedef vector<int> VI; const int maxn=405; const ll inf=0x3f3f3f3f; const ll mod=1e9+7; char s[maxn]; int dp[2][maxn][maxn]; int main(){ int n,K; scanf("%d%d%s",&n,&K,s+1); memset(dp,0x3f,sizeof(dp)); dp[0][0][0]=0; for(int i=1;i<=n;i++){ memset(dp[i%2],0x3f,sizeof(dp[i%2])); for(int j=i;j>=0;j--){ for(int k=0;k<=j;k++){ dp[i%2][j][0]=min(dp[i%2][j][0],dp[(i+1)%2][j][k]); if(s[i]=='1'&&k>0){ dp[i%2][j][k]=min(dp[i%2][j][k],dp[(i+1)%2][j-1][k-1]+k); } } } } for(int i=n;i>=0;i--){ for(int j=i;j>=0;j--){ if(dp[n%2][i][j]<=K){ printf("%d\n",i);return 0; } } } return 0; }
D 华华和月月逛公园
这是tarjan求割边的模板题,因为之前没遇到这种题所以想了很久,最后才知道是用tarjan来求的。
割边:如果删掉这条边图就不连通了,那么这条边就是割边。
这道题就是求有多少条割边,然后取m减去割边数量。
怎么求?通过tarjan求强连通分量就行了,但是以前的tarjan是求有向图的,无向图怎么办。
挺简单的,首先我们知道dfn是dfs序,low是连通分量里面的最小值。
转移:
如果下一个点没被访问过:low[u]=min(low[u],low[to])
如果访问过并且不是父亲节点:low[u]=min(low[u],dfn[to])
这部分跟有向图基本上是一样的,判断割边只需要判断low[to]是不是大于dfn[u]就好,为什么?
因为low[to]如果大于dfn[u],说明通过这条边只能连到dfs序之后的点,不会回到之前的点,既然回不去,那么这条边就很明显就是割边了,因为只有这一条路。
代码:
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<vector> #include<cstring> #include<stack> #define fs first #define se second #define pb push_back #define cppio ios::sync_with_stdio(false);cin.tie(0) #define eps 1e-7 using namespace std; typedef long long ll; typedef pair<int,int> pii; typedef vector<int> VI; const int maxn=5e5+6; const ll inf=0x3f3f3f3f; const ll mod=1e9+7; VI edge[maxn]; int dfn[maxn]; int low[maxn]; int c=0; int x=0; void dfs(int u,int fa){ dfn[u]=low[u]=++c; for(int i=0;i<edge[u].size();i++){ int to=edge[u][i]; if(!dfn[to]){ dfs(to,u); low[u]=min(low[u],low[to]); if(low[to]>dfn[u]) x++; } else if(to!=fa) low[u]=min(low[u],dfn[to]); } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=0;i<m;i++){ int x,y; scanf("%d%d",&x,&y); edge[x].pb(y); edge[y].pb(x); } dfs(1,0); printf("%d",m-x); return 0; }
E 数字比较
本场比赛的签到题,就是取一个log就好了,这样就变成ylogx和xlogy,判断一下就行。
代码:
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<vector> #include<cstring> #include<stack> #define fs first #define se second #define pb push_back #define cppio ios::sync_with_stdio(false);cin.tie(0) #define eps 1e-7 using namespace std; typedef long long ll; typedef pair<int,int> pii; typedef vector<int> VI; const int maxn=1e6+6; const ll inf=0x3f3f3f3f; const ll mod=1e9+7; int main(){ ll x,y; scanf("%lld%lld",&x,&y); if(abs(y*log(x)-x*log(y))<eps) printf("="); else if(y*log(x)>x*log(y)) printf(">"); else printf("<"); return 0; }