4月11日省赛,给自己加油!在bin神的交流群里认识了一位郑州轻工业学院的巨巨,可以面基了。。。一起去北京吧
国王的烦恼。前面连通的之后不连通的明显的用并查集处理啊。。。但是是使用逆序的并查集,即为之后不连通的,过了一天之后加上这个桥就连通了那么ans++。看看代码就懂了的。。。逆序并查集用的很多的,还有带权值并查集的使用。。食物链是个特别经典的题(当然也有不带权值的巧妙做法)
const int maxn=10050; const int maxm=100050; int fa[maxn],n,m; struct node{ int u,v,day; }edge[maxm]; int cmp(node a,node b){ return a.day>b.day; } int getf(int x){ if (x==fa[x]) return x; return fa[x]=getf(fa[x]); } int main(){ //freopen("input.txt","r",stdin); int i,j,k; int day=0,ans=0; scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].day); day=max(day,edge[i].day); } for(i=1;i<=n;i++) fa[i]=i; i=1; sort(edge+1,edge+m+1,cmp); while(day>0){ while(edge[i].day==day){ int u=getf(edge[i].u),v=getf(edge[i].v); if (u!=v){ fa[u]=v; ans++; break; } else i++; } while(edge[i].day==day){ int u=getf(edge[i].u),v=getf(edge[i].v); fa[u]=v; i++; } day--; } printf("%d\n",ans); return 0; }
网址寻路:看到题就知道又是dfs。。。蓝桥杯暴力方法万岁!细节:层数已经固定好了,为3!另外,起点和终点可以是相同的要特殊判断,而且vis数组不能修改标记
struct node{ int to,next; }edge[200050]; int n,m; int tot=0,ans=0; int head[100050]; int vis[100050]; int Print[10]; void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void print(){ for(int i=0;i<=3;i++) printf("%d%c",Print[i],i==3?'\n':' '); } void dfs(int start,int pos,int num){ if (num==4){ ans++; //print();方便自己调试,看到完整的路径过程 return; } int k; for(k=head[pos];k!=-1;k=edge[k].next){ int v=edge[k].to; if (!vis[v]){ vis[v]=1; Print[num]=v; dfs(start,v,num+1); vis[v]=0; } else if (v==start&&num==3){ Print[num]=v; dfs(start,v,num+1); //跟上面的唯一区别是不用标记vis //因为已经判定过,下一步输出而且这个点就是起点所以不会导致死循环 //而且vis也不能置0,置0会导致1-2-1-2的情况出现 // } } } int main(){ //freopen("input.txt","r",stdin); int i,j,k,u,v; memset(head,-1,sizeof(head)); memset(edge,0,sizeof(edge)); memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); while(m--){ scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); //较大的n和m值一般要学会放弃矩阵,用邻接表 } for(i=1;i<=n;i++){ vis[i]=1; Print[0]=i; dfs(i,i,1); vis[i]=0; } printf("%d\n",ans); return 0; }
最大子阵。比较难的想到的DP题。大家应该会前缀和的做法吧,如果是一维数组中求最大连续子序列和怎么做的。。就是把每个的前缀放进去然后循环一次相减就好了是吧。所以呢,把每行都求出前缀和。然后暴力各类连续行,然后去求单行的最大连续子序列和。比如行数为3,那么我需要求1,2,3,1和2,2和3,1和2和3,每个逗号分开,把这些行中的列号相对的数相加得到一个新行,求此行的最大。。。
const long long maxnum=550; long long num[maxnum]; long long ans; long long a[maxnum][maxnum]; long long n,m; void calc(){ long long i,j,k,l; long long temp=num[1],Max=num[1]; for(i=2;i<=m;i++){ if (temp>0) temp+=num[i]; else temp=num[i]; Max=max(Max,temp); } ans=max(ans,Max); } int main(){ //freopen("input.txt","r",stdin); long long i,j,k,l; scanf("%I64d%I64d",&n,&m); for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%I64d",&a[i][j]); ans=-99999999; for(i=1;i<=n;i++){ memset(num,0,sizeof(num)); for(j=i;j<=n;j++){ long long sum=0; for(k=1;k<=m;k++) num[k]+=a[j][k]; calc(); } } printf("%I64d\n",ans); return 0; }
方法:两次dfs。第一次:从任意点出发,找到离该点距离最大的点i;第二次,从i点出发,找到离i的最大距离点j
注意:本题数据量比较大,必须学会使用邻接表。。。矩阵实在太浪费空间了
int dis[10050][10050]; int vis[12000]; int n,i,j,k; int ans=0; void dfs(int pos,int dist){ int i,j,k; ans=max(dist,ans); for(k=1;k<=n;k++) if (dis[pos][k]&&!vis[k]){ vis[k]=1; dfs(k,dist+dis[pos][k]); } } int main(){ freopen("input.txt","r",stdin); memset(dis,0,sizeof(dis)); int u,v,cost; scanf("%d",&n); for(i=1;i<=n-1;i++){ scanf("%d%d%d",&u,&v,&cost); dis[v][u]=dis[u][v]=cost; } for(i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); vis[i]=1; dfs(i,0); } //printf("%d\n",ans); printf("%d\n",ans*10+ans*(1+ans)/2); return 0; }
小朋友排队:跟逆序对一样的思路,二分的归并查找