zoukankan      html  css  js  c++  java
  • 4.28(TG模拟赛)总结

    1.挖地雷

    题目背景

    NOIp1996提高组第三题

    题目描述

    在一个地图上有N个地窖(N≤20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

    输入输出格式

    输入格式:

    有若干行。

    第1行只有一个数字,表示地窖的个数N。

    第2行有N个数,分别表示每个地窖中的地雷个数。

    第3行至第N+1行表示地窖之间的连接情况:

    第3行有n-1个数(0或1),表示第一个地窖至第2个、第3个、…、第n个地窖有否路径连接。如第3行为011000…0,则表示第1个地窖至第2个地窖有路径,至第3个地窖有路径,至第4个地窖、第5个、…、第n个地窖没有路径。

    第4行有n−2个数,表示第二个地窖至第3个、第4个、…、第n个地窖有否路径连接。

    … …

    第n+1行有1个数,表示第n−1个地窖至第n个地窖有否路径连接。(为0表示没有路径,为1表示有路径)。

    输出格式:

    有两行

    第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。

    第二行只有一个数,表示能挖到的最多地雷数。

    输入输出样例:

    输入样例#1:

    5
    10 8 4 7 6
    1 1 1 0
    0 0 0
    1 1
    1

    输出样例#1:

    1 3 4 5
    27

    开始犯傻:

    说实在的,考前,刚好打了一半这道题的dfs,但是考试之后打怎么也不对,于是打了个一本通的dp,我好LJ。

    (1)dfs的心里路程就是定义一个bool型函数判断它还能不能挖,dfs函数部分判断如果不能继续挖下去,并且挖到的地雷数比之前的还多,就用opt记录路径pre,如果还能挖下去,就for循环找下一个能挖的地方,然后b改变标记,继续dfs,注意回溯b的标记。(码风可能有些奇怪

    #include"bits/stdc++.h"
    using namespace std;
    
    inline int read() {
    	int num=0,f=1;
    	char c=getchar();
    	for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    	for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    	return num*f;
    }
    
    int n;
    int ans=0;
    #define N 22
    int a[N];
    int mp[N][N];
    #define INF 0x3f3f3f3f
    int pre[N];
    bool b[N];
    int cnt;
    int opt[N];
    
    int print(int x) {
    	if(!pre[x]) cout<<pre[x]<<' ';
    	else print(pre[x]);
    }
    
    bool check(int x) {
    	for(int i=1; i<=n; i++)
    		if(mp[x][i] && !b[i]) return false;
    	return true;
    }
    
    void dfs(int x,int step,int sum) {
    	if(check(x)) {
    		if(sum>ans) {
    			ans=sum;
    			cnt=step;
    			for(int i=1; i<=step; i++)
    				opt[i]=pre[i];
    		}
    		return ;
    	}
    	for(int i=1; i<=n; i++) {
    		if(!b[i] && mp[x][i]) {
    			b[i]=1;
    			pre[step+1]=i;
    			dfs(i,step+1,sum+a[i]);
    			b[i]=0;
    		}
    	}
    }
    
    void init() {
    	for(int i=1; i<=n; i++) pre[i]=0;
    	for(int i=1; i<=n; i++) b[i]=false;
    }
    
    int main() {
    	n=read();
    	init();
    	for(int i=1; i<=n; i++) a[i]=read();
    	for(int i=1; i<=n-1; i++) {
    		for(int j=i+1; j<=n; j++) {
    			cin>>mp[i][j];
    		}
    	}
    	for(int i=1; i<=n; i++) {
    		b[i]=1;
    		pre[1]=i;
    		dfs(i,1,a[i]);
    		b[i]=0;
    	}
    	for(int i=1; i<=cnt; i++)
    		cout<<opt[i]<<' ';
    	cout<<endl<<ans;
    	return 0;
    }
    

    (2)dp的心里路程是逆推,状态转移方程是f[i]=max{f[j]+h[i]}(mp[i][j]=1,i<j<=n)。

    #include<stack>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<cctype>
    #include<list>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define N 222
    using namespace std;
    int n;
    int mp[N][N];
    int h[N];
    int f[N];
    int ans=0;
    int k=0;
    int a[N];
    
    inline int read() {
    	int num=0,f=1;
    	char c=getchar();
    	for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    	for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    	return num*f;
    }
    
    int main() {
    	/*	freopen("lei.in","r",stdin);
    		freopen("lei.out","w",stdout);*/
    	n=read();
    	for(int i=1; i<=n; i++) cin>>h[i],f[i]=h[i];
    	int x,y;
    	/*	while(scanf("%d",&x)!=0&&scanf("%d",&y)!=0) {
    			mp[x][y]=1;
    		}*/
    	for(int i=1; i<=n; i++)
    		for(int j=i+1; j<=n; j++) {
    			int ww;
    			cin>>ww;
    			if(ww==1)
    				mp[i][j]=1;
    		}
    	a[n]=0;
    	for(int i=n-1; i>0; i--)
    		for(int j=i+1; j<=n; j++)
    			if(mp[i][j]&&f[j]+h[i]>f[i]) {
    				f[i]=f[j]+h[i];
    				a[i]=j;
    			}
    	for(int i=1; i<=n; i++)
    		if(ans<f[i]) {
    			ans=f[i];
    			k=i;
    		}
    	cout<<k;
    	k=a[k];
    	while(k) {
    		cout<<" "<<k;
    		k=a[k];
    	}
    	cout<<"
    "<<ans<<'
    ';
    	return 0;
    }
    

    (有木有感觉我的代码很眼熟,是的,它就是一本通的嘿嘿Q


    2.跳石头

    题目背景

    一年一度的“跳石头”比赛又要开始了!

    题目描述

    这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

    为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

    输入输出格式

    输入格式:

    第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 (L geq 1)(N geq M geq 0)
    接下来 N 行,每行一个整数,第 i 行的整数(D_i( 0 < D_i < L)), 表示第i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

    输出格式:

    一个整数,即最短跳跃距离的最大值。

    输入输出样例:

    输入样例#1:

    25 5 2
    2
    11
    14
    17
    21

    输出样例#1:

    4

    说明:

    输入输出样例1说明:将与起点距离为2和14的两个岩石移走后,最短的跳跃距离为 4(从与起点距离17的岩石跳到距离21的岩石,或者从距离21的岩石跳到终点)。

    另:对于20%的数据,0 ≤ M ≤ N ≤ 10。

    对于50%的数据,0 ≤ M ≤ N ≤ 100。

    对于100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

    开始犯傻:

    这道题用到了小a学长讲的二分答案。
    于是,在这里献丑啦。总结一下二分答案:

    1. 求最大的最小值
    1. 求最小的最大值
    1. 求满足条件下的最大(最小)值
    1. 求最靠近一个值的值
    1. 求最小的能满足条件的代价

    附上一个接近万能的二分模板:

     int erfen(int x){
    	int l=1,r=maxn,ans=0;
    	while(l<=r){
    		int mid=(l+r) >> 1;
    		if(check(mid)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    

    再附上一位dalao的讲解:

    二分答案应该是在一个单调闭区间上进行的。也就是说,二分答案最后得到的答案应该是一个确定值,而不是像搜索那样会出现多解。二分一般用来解决最优解问题。刚才我们说单调性,那么这个单调性应该体现在哪里呢?

    可以这样想,在一个区间上,有很多数,这些数可能是我们这些问题的解,换句话说,这里有很多不合法的解,也有很多合法的解。我们只考虑合法解,并称之为可行解。考虑所有可行解,我们肯定是要从这些可行解中找到一个最好的作为我们的答案, 这个答案我们称之为最优解。

    最优解一定可行,但可行解不一定最优。我们假设整个序列具有单调性,且一个数x为可行解,那么一般的,所有的x'(x'<x)都是可行解。并且,如果有一个数y是非法解,那么一般的,所有的y'(y'>y)都是非法解。

    接下来code就很好搞啦~
    值得注意的一点是,它终点也是有石头的~
    check函数判断,详见代码啦~

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stack>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<cctype>
    #include<list>
    #define M 100099
    #define INF 0x3f3f3f3f
    using namespace std;
    long long int dis;
    int n,m;
    int c[M];
    int minn=INF;
    int maxn=-INF;
    int ans=0;
    
    inline int read() {
    	int num=0,f=1;
    	char c=getchar();
    	for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    	for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    	return num*f;
    }
    
    int check(int x) {//为什么要用二分,因为具有单调性并且有界(骚~
    	int num=0,now=0;//num已经搬走的,now目前的
    	for(int i=1; i<=n+1; i++) {//要到终点那块
    		if(c[i]-now<x) {//如果比当前枚举的最小距离还小就搬走
    			num++;
    		} else now=c[i];
    	}
    	if(num>m) return 0;//超过题目要求的
    }
    
    void erfen(int x,int y) {
    	int l=0,r=dis;
    	while(l<=r) {
    		int	mid=(l+r) >> 1;
    		if(check(mid)) {
    			l=mid+1;
    			ans=mid;
    		} else {
    			r=mid-1;
    		}
    	}
    }
    
    int main() {
    	/*freopen("stone.in","r",stdin);
    	freopen("stone.out","w",stdout);*/
    	dis=read();
    	n=read();
    	m=read();
    	int l=0,r=dis;
    	for(int i=1; i<=n; i++) cin>>c[i];
    	c[n+1]=dis;
    	while(l<=r) {
    		int mid=(l+r) >> 1;
    		if(check(mid)) {
    			l=mid+1;
    			ans=mid;
    		} else {
    			r=mid-1;
    		}
    	}
    	cout<<ans;
    }
    

    3.铺设道路

    题目描述

    春春是一名道路工程师,负责铺设一条长度为 n 的道路。

    铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 (d_i)
    春春每天可以选择一段连续区间[L,R],填充这段区间中的每块区域,让其下陷深度减少 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 。

    春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 。

    输入输出格式

    输入格式:

    输入文件包含两行,第一行包含一个整数 n,表示道路的长度。 第二行包含 n 个整数,相邻两数间用一个空格隔开,第 i 个整数为 (d_i)

    输出格式:

    输出文件仅包含一个整数,即最少需要多少天才能完成任务。

    输入输出样例

    输入样例#1:

    6
    4 3 2 5 3 5

    输出样例#1:

    9

    说明

    【样例解释】

    一种可行的最佳方案是,依次选择: [1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]。

    【数据规模与约定】

    对于 30% 的数据,1 ≤ n ≤ 10 ;
    对于 70% 的数据,1 ≤ n ≤ 1000;
    对于 100% 的数据,1 ≤ n ≤ 100000 , 0 ≤ (d_i) ≤ 10000。

    开始犯傻:

    这道题我居然**似的没看出来是原题(指积木大赛)(CCF:我抄我自己)。主要的心里路程还是很善良的,主要的code也就是两三行Q。咳咳,正经一点,两种思路。

    (1)贪心思路:若a[i]>a[i-1],计数器sum+=a[i]-a[i-1];

    验证:假设现在有一个坑,但旁边又有一个坑。

    你肯定会选择把两个同时减1;

    那么小的坑肯定会被大的坑“带着”填掉。

    大的坑也会减少a[i]-a[i-1]的深度,可以说是“免费的”;

    所以这样贪心是对的;(来自dalao)

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[100005];
    long long ans=0;
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)     cin>>a[i];
        for(int i=2;i<=n;i++)     if(a[i]>a[i-1]) ans+=a[i]-a[i-1];
        cout<<ans+a[1];
        return 0;
    }
    

    (2)递推思路:
    用f[i]表示前i个坑所铺设的最少天数

    那么要做的只需比较一下当前的a[i](就是坑的深度)和a[i−1],分两种情况:

    如果a[i]<=a[i-1],那么在填a[i−1]时就可以顺便把a[i]填上,这样显然更优,所以f[i]=f[i-1];

    否则的话,那么在填a[i−1]时肯定要尽量把a[i]一块填上,a[i]剩余的就单独填。。

    所以,f[i]=f[i−1]+(a[i]−a[i−1])。

    初始化f[1]=a[1],向后推就行了。(依旧来自dalao)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iomanip>
    #include<stack>
    #include<queue>
    #include<map>
    #include<list>
    #include<set>
    #include<cctype>
    #include<bitset>
    #define N 100111
    int n,opt[N],uxv[N];
    
    inline int read() {
    	int num=0,f=1;
    	char c=getchar();
    	for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    	for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    	return num*f;
    }
    
    int main(){
    	//freopen("road.in","r",stdin); freopen("road.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++) uxv[i]=read();
    	opt[1]=uxv[1];
    	for(int i=2;i<=n;i++) uxv[i]<=uxv[i-1] ? opt[i]=opt[i-1] : opt[i]=opt[i-1]+(uxv[i]-uxv[i-1]);
    	printf("%d",opt[n]);
    }
    

    4.花匠

    题目描述

    花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。

    具体而言,栋栋的花的高度可以看成一列整数(h_1),(h_2),...,(h_n)。设当一部分花被移走后,剩下的花的高度依次为(g_1),(g_2),...,(g_m),则栋栋希望下面两个条件中至少有一个满足:

    条件 A:对于所有(g_{2i})>(g_{2i-1}),(g_{2i})>(g_{2i+1})
    条件 B:对于所有(g_{2i})<(g_{2i-1}),(g_{2i})<(g_{2i+1})

    注意上面两个条件在m=1时同时满足,当m>1时最多有一个能满足。

    请问,栋栋最多能将多少株花留在原地。

    输入输出格式

    输入格式:

    第一行包含一个整数n,表示开始时花的株数。

    第二行包含n个整数,依次为(h_1),(h_2),...,(h_n),表示每株花的高度。

    输出格式:

    一个整数m,表示最多能留在原地的花的株数。

    输入输出样例

    输入样例#1:

    5
    5 3 2 1 2

    输出样例#1:

    3

    说明

    【输入输出样例说明】

    有多种方法可以正好保留3株花,例如,留下第1、4、5株,高度分别为5、1、2,满足条件 B。

    【数据范围】

    对于 20%的数据,n ≤ 10;

    对于 30%的数据,n ≤ 25;

    对于 70%的数据,n ≤ 1000,0 ≤ (h_i)≤ 1000;

    对于 100%的数据,1≤n≤100,000,0≤(h_i)≤1,000,000,所有的(h_i)随机生成,所有随机数服从某区间内的均匀分布。

    开始犯傻:

    考前做luogu上DP的题,然后翻题解的时候看到个双倍经验(眼前一亮),发现那道题的双倍经验就是这道,但是当时只是看了看题目,觉得真的是双倍经验,然后默默点了加入任务计划。(我:MMP??)
    废话不多bb,画个图来解释???(充满善意的微笑)
    此处输入图片的描述

    然后呐,就差不多是这个意思。要不再来解释下样例??(好的,又一次)

    此处输入图片的描述

    好的,差不多就是这个样子。

    然后就非常的好理解了,让你找一个最长的大波浪序列,记录下波谷波峰就好啦。(这是dalao的想法,以下才是我的)用两个数组分别记录到i下降和到i上升。
    这是dalao的代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,h[1000005],ans=1;bool con;
    int main()
    {
        cin>>n;for(int i=1;i<=n;i++) cin>>h[i];
        if(h[2]>=h[1]) con=1;
        for(int i=1;i<=n;i++)
        {
            if(con==0&&i==n) {ans++;break;}
            if(con==1) if(h[i+1]<h[i]){ans++;con=0;continue;}
            if(con==0) if(h[i+1]>h[i]) {ans++;con=1;continue;}
        }
        cout<<ans; 
    }
    

    这是我的代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<stack>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<set>
    #include<map>
    #include<cctype>
    #include<list>
    #define N 100055
    using namespace std;
    int h[N],f[N][3];
    
    inline int read() {
    	int num=0,f=1;
    	char c=getchar();
    	for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
    	for(; isdigit(c); c=getchar()) num=num*10+c-'0';
    	return num*f;
    }
    
    int main() {
    	/*freopen("flower.in","r",stdin);
    	freopen("flower.out","w",stdin);*/
    	int n,ans=0;
    	n=read();
    	memset(f,0,sizeof(f));
    	for(int i=1; i<=n; i++) {
    		h[i]=read();
    		//	f[i][0]=f[i][4]=1;
    	}
    	f[1][0]=f[1][5]=1;
    	for(int i=2; i<=n; i++) {
    		if(h[i]>h[i-1])
    			f[i][0]=f[i-1][6]+1;
    		else f[i][0]=f[i-1][0];
    		if(h[i]<h[i-1])
    			f[i][7]=f[i-1][0]+1;
    		else f[i][8]=f[i-1][9];
    	}
    	ans=max(f[n][0],f[n][10]);
    	cout<<ans<<endl;
    }
    

    5.货币系统

    题目描述

    在网友的国度中共有 nn 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n、面额数组为 a[1..n] 的货币系统记作 (n,a)。

    在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i] imes t[i]a[i]×t[i] 的和为 xx。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 xx 不能被该货币系统表示出。例如在货币系统 n=3n=3, a=[2,5,9]a=[2,5,9] 中,金额 1,31,3 就无法被表示出来。

    两个货币系统 (n,a)(n,a) 和 (m,b)(m,b) 是等价的,当且仅当对于任意非负整数 xx,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

    现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b)(m,b),满足 (m,b)(m,b) 与原来的货币系统 (n,a)(n,a) 等价,且 mm 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 mm。

    输入输出格式
    输入格式:
    输入文件的第一行包含一个整数 TT,表示数据的组数。

    接下来按照如下格式分别给出 TT 组数据。 每组数据的第一行包含一个正整数 nn。接下来一行包含 nn 个由空格隔开的正整数 a[i]a[i]。

    输出格式:
    输出文件共有 TT 行,对于每组数据,输出一行一个正整数,表示所有与 (n,a)(n,a) 等价的货币系统 (m,b)(m,b) 中,最小的 mm。

    输入输出样例
    输入样例#1:
    2
    4
    3 19 10 6
    5
    11 29 13 19 17
    输出样例#1:
    2
    5

    说明

    在第一组数据中,货币系统(2,[3,10])和给出的货币系统(n,a)等价,并可以验证不存在 m < 2 的等价的货币系统,因此答案为 22。
    在第二组数据中,可以验证不存在 m < n 的等价的货币系统,因此答案为 5。

    【数据范围与约定】

    ![][11]
    对于 100% 的数据,满足 1 ≤ T ≤ 20, n,a[i] ≥ 1。

    开始犯傻:

    考试时,题面我都没搞懂,我是来搞笑的嘛Q?改题时:逐渐开始佩服我自己,还是Orz一下我们的loceaner大佬吧。

    附上dalao的思路:

    先解释一下样例1中第一组数据的3,10,19,6等价于3,10的原因
    6=3+3,19=3+3+3+10
    即13,19都可以被凑出来
    而第二组中的5个数都不能将其他的数凑出来(或被凑出来)
    所以直接输出了5
    所以就排序看看能不能凑出来就好啦
    a[i]=0表示没有i这个数,a[i]=1表示可以凑出i这个数,a[i]=2表示本身就有i这个数
    如果处理完成之后a[1~n]中还有等于2的(即本身就有这个数并且不能凑(只能他凑别人))
    就让ans++,最后输出就好了

        说实在的我看懂了,但是我不知道为啥我打不出来,惨
    

    附上dalao的代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<deque>
    #define INF 0x3f3f3f3f
    using namespace std;
    
    int a[25001];
    int b[101];
    int t,n,ans=0;
    
    inline int read() {
    	char c=getchar();
    	int x=0,f=1;
    	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} 
    	while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    	return x*f;
    }
    
    int main() {
    	//freopen("money.in","r",stdin);
    	//freopen("money.out","w",stdout);
    	t=read();
    	while (t--) {
    		ans=0;
    		memset(a,0,sizeof(a));
    		scanf("%d",&n);
    		for (int i=1; i<=n; i++) {
    			b[i]=read();
    			a[b[i]]=2;//本身就有b[i]这个数;
    		}
    		sort(b+1,b+1+n);//从小到大排序 
    		for (int i=1; i<=b[n]; i++) {
    			if(a[i]>0) {//如果可以凑出i
    						//那么也可以凑出i+b[j] 
    				for(int j=1; j<=n; j++) {
    					if(i+b[j]<=b[n])//排序之后,b[n]一定是b数组中最大的数,在这里防止越界 
    						a[i+b[j]]=1;
    					else break;//越界的话直接退出 
    				}
    			}
    		}
    		for(int i=1; i<=b[n]; i++)
    			if(a[i]==2) ans++;//统计a[i]==2的个数输出 
    		printf("%d
    ",ans);
    	}
    }
    

    我太LJ了,所以代码还是看dalao的比较实在。


    6.寻找道路

    题目描述

    在有向图 G 中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

    路径上的所有点的出边所指向的点都直接或间接与终点连通。
    在满足条件1的情况下使路径最短。
    注意:图 G 中可能存在重边和自环,题目保证终点没有出边。

    请你输出符合条件的路径的长度。

    输入输出格式

    输入格式:

    第一行有两个用一个空格隔开的整数 n 和 m,表示图有 n 个点和 m 条边。

    接下来的 m 行每行 2 个整数 x,y之间用一个空格隔开,表示有一条边从点 x 指向点y。

    最后一行有两个用一个空格隔开的整数 s, t表示起点为 s,终点为 t。

    输出格式:

    输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出−1。

    输入输出样例

    输入样例#1:

    3 2
    1 2
    2 1
    1 3

    输出样例#1:

    -1

    输入样例#2:

    6 6
    1 2
    1 3
    2 6
    2 5
    4 5
    3 4
    1 5

    输出样例#2:

    3

    说明

    解释1:

    此处输入图片的描述
    如上图所示,箭头表示有向道路,圆点表示城市。起点1与终点3不连通,所以满足题目描述的路径不存在,故输出−1 。

    解释2:

    此处输入图片的描述
    如上图所示,满足条件的路径为1->3->4->5。注意点2不能在答案路径中,因为点2连了一条边到点6,而点6 不与终点5 连通。

    【数据范围】

    对于30%的数据,0 < n ≤ 10,0 < m ≤ 20;

    对于60%的数据,0 < n ≤ 100,0 < m ≤ 2000;

    对于100%的数据,0 < n ≤ 10000 ,0 < m ≤ 200000,0 < x , y , s , t ≤ n,x,s ≠ t。

    开始犯傻:

    我不会这道题!!!
    我不会这道!!!
    我不会这!!!
    我不会!!!
    我不!!!
    我!!!
    !!!
    !!

    所以直接放代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<iomanip>
    #include<stack>
    #include<queue>
    #include<map>
    #include<list>
    #include<set>
    #include<cctype>
    #include<bitset>
    #define N 20000
    #define M 400000
    #define Q 300000
    #define INF 0x7fffffff
    using namespace std;
    struct edge {
        int u,v,next;
        //int num;
    } a[M];
    int head[N];
    bool vis[N];
    int num=0;
    int n,m;
    int s,t;
    bool lala[N],mama[N];
    int opt[N],uxv[N];
    
    void add_edge(int x,int y) {
        a[++num].u=x;
        a[num].v=y;
        a[num].next=head[x];
        head[x]=num;
    }
    
    inline int read() {
        int num=0,f=1;
        char c=getchar();
        for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;
        for(; isdigit(c); c=getchar()) num=num*10+c-'0';
        return num*f;
    }
    
    int SPFA(int x,int y) {
        int headd=-1,tail=0;
        for(int i=1; i<=n; i++) opt[i]=INF;
        opt[x]=0;
        uxv[0]=x;
        vis[x]=mama[x]=true;
        while(headd!=tail) {
            headd=(headd+1)%M;
            int uu=uxv[headd];
            int vv;
            for(int i=head[uu]; i; i=a[i].next)
                if(opt[uu]+1<opt[vv=a[i].v]) {
                    opt[vv]=opt[uu]+1;
                    if(!vis[vv]) {
                        tail=(tail+1)%M;
                        uxv[tail]=vv;
                        vis[vv]=true;
                        mama[vv]=true;
                    }
                }
            vis[uu]=false;
        }
        if(opt[y]==INF) 
        return -1;
        return opt[y];
    }
    
    int main() {
        /*freopen("road.in","r",stdin);
        freopen("road.out","w",stdout);*/
        n=read();
        m=read();
        for(int i=1; i<=m; i++) {
            int x,y;
            x=read();
            y=read();
            add_edge(y,x);
        }
        s=read();
        t=read();
        SPFA(t,n+1);
        for(int i=1; i<=n; i++) lala[i]=mama[i];
        for(int i=1; i<=m; i++)
            if(mama[a[i].u]==false)
                lala[a[i].v]=false;
        memset(head,0,sizeof(head));
        memset(vis,false,sizeof(vis));
        num=0;
        for(int i=1; i<=m; i++)
            if(lala[a[i].u] && lala[a[i].v])
                add_edge(a[i].v,a[i].u);
        printf("%d
    ",SPFA(s,t));
        return 0;
    }
    
    综上所述:我就是个弟弟。
  • 相关阅读:
    mysql创建用户,并赋予权限:只能查某个数据库中的某张表(只读)
    Fastjson toJSONString用单引号进行转换
    MyBatis传入参数为list、数组、map写法
    进制GB和GiB的区别
    leaflet 根据一个经纬度及距离角度,算出另外一个经纬度
    ubuntu下安装YApi
    Oracle 存储过程测试
    Oracle两种临时表的创建与使用详解
    一月到十二月的英文
    spring framework各个版本下载网址
  • 原文地址:https://www.cnblogs.com/morbidity/p/10790388.html
Copyright © 2011-2022 走看看