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;
}
波动数列:dp[i][j]考虑,其中j表示对n的余数
小朋友排队:跟逆序对一样的思路,二分的归并查找