• 专题:CF图论杂题


    题目是来自HZW的博客(构造题我是各种不会...)

    Solved 1 / 1 A CodeForces 500A New Year Transportation
    Solved 1 / 1 B CodeForces 437C The Child and Toy
    Solved 1 / 2 C CodeForces 510C Fox And Names
    Solved 1 / 3 D CodeForces 475B Strongly Connected City
    Solved 1 / 2 E CodeForces 639B Bear and Forgotten Tree 3
    Solved 1 / 3 F CodeForces 623A Graph and String
    Solved 1 / 20 G CodeForces 449B Jzzhu and Cities
    Solved 1 / 4 H CodeForces 543B Destroying Roads

    A

    给一些线性排列的节点,每个节点有向后面第ai个节点的单向边,问是否能从1到T

    1n,ai10^5

    //直接模拟,1能到的点,所能到的点也是能到的,最后检查t
    //
    #include <string.h> #include <iostream> #include <algorithm> #include <stdio.h> using namespace std; const int maxn=1e5+10; const int maxm=1e6+10; const int INF=0x3f3f3f3f; int casn,n,m,k; int num[maxn]; int vis[maxn]; int main(){ //#define test #ifdef test freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #endif while(cin>>n>>m){ memset(vis,0,sizeof vis); memset(num,0,sizeof num); for(int i=1;i<n;i++){ cin>>num[i]; } vis[1]=1; for(int i=1;i<n;i++){ if(!vis[i])continue; vis[i+num[i]]=true; } if(vis[m])puts("YES"); else puts("NO"); } #ifdef test fclose(stdin);fclose(stdout);system("out.txt"); #endif return 0; }

    B

    n个点.m条边的无向图,输入保证不含重边

    删除一个点的代价是与这个点相邻的剩余点的权值和,求将所有点删除的最小代价

    /*
    很巧妙的思路,核心点在于,假设a,b两点相连
    若先删除a,最终代价就是 b的权值+删除剩下所有点的花费
    若先删除b,最终代价就是 a的权值+删除剩下所有点的花费
    这个规则对于所有的相邻点都成立,简单来说,我们可以通过改变删除顺序,贪心的得到最小的代价
    简单来说,对于任意一个边,其权值较大的一个端点就是我们先要删除的,也就是说代价是所有边中权值较小的一端
    */
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    int num[maxn];
    int vis[maxn];
    
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
    	while(cin>>n>>m){
    		int ans=0;
    		for(int i=1;i<=n;i++) cin>>num[i];
    		while(m--){
    			int a,b;
    			cin>>a>>b;
    			ans+=min(num[a],num[b]);
    		}
    		cout<<ans<<endl;
    	}
    
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }
    

    C

    给定n个字符串,问是否存在一种自字母表,使得让这些字符串按照输入顺序排列满足字典序

    1n,|S|100

     

    直接按照输入顺序遍历,并建立一条从大字母到小字母的边,并记录所有点的入度

    我们考虑dfs

    对于入度为0的字母,自然放在新字母表的前端,然后把该字母节点删去,也就是把所有小于它的字母入度减一,如果入度为零了,自然也就可以放进剩下字母表的前端了

    直到所有字母都进入字母表,进行检查是否合法,如果有的字母入度不为零,也就是还没入字母表,不存在答案

    注意如果一个字符串是另一个字符串的前缀,它的字典序是更小的,也就是说如果一个字符串比他的前缀还靠前,不存在答案

    其实这个过程也就是一个拓扑排序

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    #include <vector>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    int stk[maxn];
    int top;
    int deg[maxn];
    int alp[123];
    vector<int>g[maxn];
    char s[123][123];
    int ans[123];
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
    	while(cin>>n){
    		top=0;
    		for(int i=0;i<30;i++)g[i].clear();
    		memset(deg,0,sizeof deg);
    //		for(int i=0;i<26;i++) alp[i]=i;
    		for(int i=0;i<n;i++){
    			cin>>s[i];
    		}
    		int flag=1;
    		for(int i=0;i<n&&flag;i++){
    			int len=strlen(s[i]);
    			for(int j=i+1;j<n&&flag;j++){
    				for(int k=0;k<len;k++){
    					if(k>=strlen(s[j])){
    						flag=0;
    						break;
    					}
    					if(s[i][k]!=s[j][k]) {
    						deg[s[j][k]-'a'+1]++;
    						g[s[i][k]-'a'+1].push_back(s[j][k]-'a'+1);
    						break;
    					}
    				}
    			}
    		}
    		for(int i=1;i<=26;i++)if(!deg[i])stk[++top]=i;
    		int cnt=0;
    		while(top){
    			int now=stk[top--];
    			alp[cnt++]=now;
    			for(int i=0;i<g[now].size();i++){
    				int to=g[now][i];
    				deg[to]--;
    				if(!deg[to]){
    					stk[++top]=to;
    				}
    			}
    		}
    		for(int i=1;i<=26;i++){
    			if(deg[i]){
    				flag=0;
    				break;
    			}
    		}
    		if(!flag)puts("Impossible");
    		else {
    			for(int i=0;i<26;i++){
    				cout<<((char)(alp[i]+'a'-1));
    			}
    			cout<<endl;
    		}
    	}
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }
    

    D

    一个网格,你可以沿着格子上的线移动,

    每一条横着贯穿的线只能向左或者向右走

    每一个竖着贯穿的线只能向上或者向下走

    问是否任意两点可达

     

    dfs(我并不会),或者tarjan(敲得很舒服),写完看题解说可以只看外围情况...太菜了Orz

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    char hor[123];
    char ver[123];
    int g[23][23][23][23];
    int vis[123][123];
    const int dir[4][2]={1,0,-1,0,0,1,0,-1};
    #define dx dir[d][0]
    #define dy dir[d][1]
    void dfs(int nx,int ny){
    	vis[nx][ny]++;
    	for(int d=0;d<4;d++){
    		int x=nx+dx;
    		int y=ny+dy;
    		if(x&&y&&x<=n&&y<=m&&g[nx][ny][x][y]&&!vis[x][y]){
    			dfs(x,y);
    		}
    	}
    }
    int main(){
    
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    	while(cin>>n>>m){
    		cin>>(hor+1)>>(ver+1);
    		memset(g,0,sizeof g);
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=m;j++){
    				if(hor[i]=='<'&&j){
    					g[i][j][i][j-1]=1;
    				}else if(hor[i]=='>'&&j<m){
    					g[i][j][i][j+1]=1;
    				}
    			}
    		}
    		for(int i=1;i<=m;i++){
    			for(int j=1;j<=n;j++){
    				if(ver[i]=='v'&&j<n){
    					g[j][i][j+1][i]=1;
    				}else if(ver[i]=='^'&&j){
    					g[j][i][j-1][i]=1;
    				}
    			}
    		}
    		int flag=1;
    		memset(vis,0,sizeof vis);
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=m;j++){
    				memset(vis,0,sizeof vis);
    				dfs(i,j);
    				for(int k=1;k<=n;k++){
    					for(int q=1;q<=m;q++){
    						if(!vis[k][q]){
    							flag=0;
    							k=q=1e9;
    						}
    					}
    				}
    				if(!flag) i=j=1e9;
    			}
    		}
    		if(flag) puts("YES");
    		else puts("NO");
    	}
    
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }
    
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    char hor[123],ver[123];
    int stk[1234],top,cnt,dfn[1234],low[1234],numc,belong[1234];
    int vis[1234];
    struct node {
    	int to,next;
    }e[maxn];
    int nume,head[1234];
    void add(int a,int b){
    	e[nume]=(node){b,head[a]};
    	head[a]=nume++;
    }
    void tdfs(int now){
    	dfn[now]=low[now]=++cnt;
    	stk[top++]=now;
    	vis[now]=1;
    	for(int i=head[now];~i;i=e[i].next){
    		int to=e[i].to;
    		if(!dfn[to]){tdfs(to);low[now]=min(low[now],low[to]);}
    		else if(vis[to]) low[now]=min(low[now],low[to]);
    	}
    	if(low[now]==dfn[now]){
    		numc++;
    		int to;
    		do{
    			to=stk[--top];
    			belong[to]=numc;
    			vis[to]=0;
    		}while(to!=now);
    	}
    }
    void tarjan(int numv=n){
    	for(int i=1;i<=numv;i++){
    		if(!dfn[i]) tdfs(i);
    	}
    }
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
    	while(cin>>n>>m){
    		nume=top=numc=cnt=0;
    		memset(head,-1,sizeof head);
    		memset(dfn,0,sizeof dfn);
    		memset(low,0,sizeof low);
    		memset(belong,0,sizeof belong);
    		memset(vis,0,sizeof vis);
    		cin>>(hor+1)>>(ver+1);
    		for(int i=1;i<=n;i++){
    			if(hor[i]=='<')
    				for(int j=2;j<=m;j++)
    					add((i-1)*m+j,(i-1)*m+j-1);
    			else for(int j=1;j<m;j++)
    				add((i-1)*m+j,(i-1)*m+j+1);
    		}
    		for(int i=1;i<=m;i++){
    			if(ver[i]=='v')
    				for(int j=1;j<n;j++)
    					add((j-1)*m+i,(j)*m+i);
    			else for(int j=2;j<=n;j++)
    					add((j-1)*m+i,(j-2)*m+i);
    		}
    		tarjan(n*m);
    		puts((numc==1)?"YES":"NO");
    	}
    
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }

    E

    给一个树的3种属性,点数,深度,直径

    让你构造出来其中一种

     

    先判断是否可行

    再去构造深度

    再去构造剩下的满足直径

    再把其他点放在..不影响之前性质的地方(我一开始写的链式前向星存图,其实只用保留前驱节点就行..)

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    struct node {int to,cost,next;}e[maxm];int head[maxn],nume;
    void add(int a,int b,int c=1){e[++nume]=(node){b,c,head[a]};head[a]=nume;}
    int pre[maxn];
    
    int main(){
    //#define test
    #ifdef test
        freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
        while(cin>>n>>k>>m){
            if(k<m||k==n||k==1&&n>2||k>2*m){
                puts("-1");
                continue;
            }
            memset(pre,0,sizeof pre);
            for(int i=2;i<=m+1;i++) pre[i]=i-1;
            (m+2<=k+1) pre[m+2]=1;
            for(int i=m+3;i<=k+1;i++) pre[i]=i-1;
            for(int i=k+2;i<=n;i++) pre[i]=m;
            for(int i=2;i<=n;i++) cout<<pre[i]<<' '<<i<<endl;
        }
    
    #ifdef test
        fclose(stdin);fclose(stdout);system("out.txt");
    #endif
        return 0;
    }

    F

    给n个点m个边

    告诉你这个图其实是一个abc三个字母的字符串构成的

    如果Si和Sj差距小于等于1,则连接一条无向边

    问一个合法解,也就是问一种合法的染色

     

    首先,记录入度

    如果入度等于n-1,也就是和所有其他点都有边,这个字母自然是b

    然后,对于任意一对没有染色的点,自然一个是a,一个是c

    然后,其中和a连接的,自然是a,反之是c

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e3+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    int deg[maxn];
    int g[maxn][maxn];
    int ans[maxn];
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
    	while(cin>>n>>m){
    		memset(g,0,sizeof g);
    		memset(deg,0,sizeof deg);
    		for(int i=0;i<m;i++){
    			int a,b;
    			scanf("%d%d",&a,&b);
    			g[a][b]=g[b][a]=1;
    			deg[a]++;
    			deg[b]++;
    		}
    		memset(ans,0,sizeof ans);
    		for(int i=1;i<=n;i++){
    			if(deg[i]==n-1) ans[i]=2;
    		}
    		for(int i=1;i<=n;i++){
    			if(ans[i]) continue;
    			for(int j=i+1;j<=n;j++){
    				if(!g[i][j]){
    					ans[i]=1;ans[j]=3;
    					for(int k=1;k<=n;k++){
    						if(k==i||k==j||ans[k]) continue;
    						if(g[k][i]) ans[k]=1;
    						if(g[k][j]) ans[k]=3;
    					}
    				}
    			}
    		}
    		int sin=1;
    		for(int i=1;i<=n;i++){
    			if(!ans[i]){
    				sin=0;
    				break;
    			}
    		}
    		for(int i=1;i<=n&&sin;i++){
    			for(int j=i+1;j<=n;j++){
    				if(g[i][j]&&abs(ans[i]-ans[j])>1){
    					sin=0;
    					break;
    				}
    				if(!g[i][j]&&abs(ans[i]-ans[j])<=1){
    					sin=0;
    					break;
    				}
    			}
    		}
    		if(sin){
    			puts("YES");
    			for(int i=1;i<=n;i++)cout<<(char)(ans[i]+'a'-1);
    			cout<<endl;
    		}
    		else puts("NO");
    	}
    
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }
    

    G

    给一个无向图,又有一些额外边是从起点直接到某些点的,存在重边

    问你最多删除多少个额外路,使得每个点到起点的最短路不变

     

    先构造图,跑一边spfa,然后如果有特殊路大于当前最短路,直接删掉

    否则枚举这个点的边,如果有和这个点相邻的点+边权等于到该点的特殊路径,也删掉

    而对于任意一点

    如果特殊路径被保存了一个,其他到该点的特殊路径也都应该被删除

    如果该点的特殊路径等于其他走普通路径的距离,其他所有到该点的特殊路径也应该被删除

    标记,剪枝一下即可

    注意,该题卡spfa,但是用spfa+slf优化可以秒杀

    当然,也可能是我这个做法比较臭(也有可能是因为重边太多了)

    能想到的还有比如记录入度,如果入度大于1且有同样长度,就删除,速度应该差不多

    还有就是先跑一遍spfa,然后加入特殊路之前先剪枝一波,然后再在原图的基础上跑一遍spfa

    或者就是把有特殊路的节点标记上,如果这个点被松弛了,自然这个地方的特殊路就会被删除,最后统计...

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    #define ll long long
    int casn,n,m,k;
    struct node {int to;int cost;int next;}e[maxm];int head[maxn],nume;
    inline void add(int a,int b,int c=1){e[++nume]=(node){b,c,head[a]};head[a]=nume;}
    int road[maxn];
    ll dis[maxn];
    bool vis[maxn];
    int que[maxn];
    int go[maxn];
    
    void spfa(int st=1,int ed=n){
      int top=0,end=0,now;
      memset(dis,INF,sizeof dis);
      memset(vis,0,sizeof vis);
      que[end++]=st,dis[st]=0;
      while(top!=end){
        now=que[top++],top%=maxn;
        vis[now]=false;
        for(int i=head[now];i;i=e[i].next){
          int to=e[i].to;
          if(now==to) continue;
          if(dis[now]+e[i].cost<dis[to]){
            dis[to]=dis[now]+e[i].cost;
            if(!vis[to]){
           	 vis[to]=true;
            	if(dis[to]<dis[que[top]]){
    		  		  top--;
    		  		  if(top==-1)top=maxn-1;
    		  		  que[top]=to;
    					}else{
    						que[end++]=to;
    						end%=maxn;
    					}
    				}
          }
        }
      }
    }
    
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    
    	cin>>n>>m>>k;
    	for(int i=0;i<m;i++){
    		int a,b,c;
    		scanf("%d%d%d",&a,&b,&c);
    		add(a,b,c);
    		add(b,a,c);
    	}
    	int ans=0;
    	int cnt=0;
    	for(int i=1;i<=k;i++){
    		int a,b;
    		scanf("%d%d",&a,&b);
    		add(1,a,b);
    		road[i]=b;
    		go[i]=a;
    	}
    	spfa();
    	memset(vis,0,sizeof vis);
    	for(int i=1;i<=k;i++){
    		if(vis[go[i]]||road[i]>dis[go[i]]){
    			ans++;
    			continue;
    		}
    		for(int j=head[go[i]];j;j=e[j].next){
    			int to=e[j].to;
    			if(dis[go[i]]==dis[to]+e[j].cost){
    				ans++;
    				break;
    			}
    		}
    		vis[go[i]]=1;
    	}
    	cout<<ans<<endl;
    
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }

    H

    给一个无权图,问你如何删除最多的边

    才能使得s1 t1的距离依然大于l1,s2 t2的距离依然大于l2

    n<=3000

     

    原题等价于最少多少条边可满足l1,l2的条件,其他的边都可以删掉

    首先如果两个路无交集,就是要保留s1 t1,s2 t2的路径边数之和

    然后n^2枚举共同路径,如果走共同路径的路径长度满足条件,就更新答案

    注意点数可以达到3000,我的floyd一直T,改成n次spfa+slf优化就ac了...原谅我spfa敲得太顺手了..

    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <stdio.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=1e6+10;
    const int INF=0x3f3f3f3f;
    int casn,n,m,k;
    struct node {int to,cost,next;}e[maxm];int head[maxn],nume;
    void add(int a,int b,int c=1){e[++nume]=(node){b,c,head[a]};head[a]=nume;}
    int s1,s2,t1,t2,l1,l2;
    int dis[3000+15][3000+15];
    bool vis[maxn];
    int que[maxn];
    void spfa(int st=1,int ed=n){
      int top=0,end=0,now;
      memset(vis,0,sizeof vis);
      que[end++]=st,dis[st][st]=0;
      while(top!=end){
        now=que[top++],top%=maxn;
        vis[now]=false;
        for(int i=head[now];i;i=e[i].next){
          int to=e[i].to;
          if(now==to) continue;
          if(dis[st][now]+e[i].cost<dis[st][to]){
            dis[st][to]=dis[st][now]+e[i].cost;
            if(!vis[to]){
           	 vis[to]=true;
            	if(dis[now][to]<dis[now][que[top]]){
    		  		top--;
    		  		if(top==-1)top=maxn-1;
    		  		que[top]=to;
    			}else{
    				que[end++]=to;
    				end%=maxn;
    			}
    		}
          }
        }
      }
    }
    
    int main(){
    //#define test
    #ifdef test
    	freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
    #endif
    	ios::sync_with_stdio(false);
    	cin>>n>>m;
    	memset(dis,0x3f,sizeof dis);
    	for(int i=0;i<m;i++){
    		int a,b;
    		cin>>a>>b;
    		add(a,b,1);
    		add(b,a,1);
    	}
    	cin>>s1>>t1>>l1;
    	cin>>s2>>t2>>l2;
    	for(int i=1;i<=n;i++){
    		spfa(i);
    	}
    	int flag=1;
    	if(dis[s1][t1]>l1||dis[s2][t2]>l2){
    		flag=0;
    	}
    	int ans=dis[s1][t1]+dis[s2][t2];
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			int d1=dis[s1][i]+dis[j][t1];
    			int d2=dis[s2][i]+dis[j][t2];
    			int mid=dis[i][j];
    			int d3=dis[s1][j]+dis[i][t1];
    			int d4=dis[s2][j]+dis[i][t2];
    			if(d1+mid<=l1&&d2+mid<=l2){
    				ans=min(ans,d1+d2+mid);
    			}
    			if(d3+mid<=l1&&d4+mid<=l2){
    				ans=min(ans,d3+d4+mid);
    			}
    			if(d1+mid<=l1&&d4+mid<=l2){
    				ans=min(ans,d1+d4+mid);
    			}
    			if(d3+mid<=l1&&d2+mid<=l2){
    				ans=min(ans,d3+d2+mid);
    			}
    		}
    	}
    	if(flag) cout<<m-ans<<endl;
    	else puts("-1");
    #ifdef test
    	fclose(stdin);fclose(stdout);system("out.txt");
    #endif
    	return 0;
    }
  • 相关阅读:
    springboot 集成redission 以及分布式锁的使用
    springboot 使用线程池
    springboot 引入logback框架
    StringRedisTemplate 使用示例,工具类
    XHR post请求下载文件
    java List对象排序
    springboot中使用resttemplate调用服务是设置header中的accept-charset
    面试
    本地包打到项目中利用maven
    sql
  • 原文地址:https://www.cnblogs.com/nervendnig/p/8669262.html
Copyright © 2020-2023  润新知