Week5图 + acm第一次双周赛补题

[蓝桥杯 2013 国 C] 危险系数

题目背景

抗日战争时期,冀中平原的地道战曾发挥重要作用。

题目描述

地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

我们来定义一个危险系数 D F ( x , y ) DF(x,y) DF(x,y)

对于两个站点 x x x y ( x ≠ y ) , y(x\neq y), y(x=y), 如果能找到一个站点 z z z,当 z z z 被敌人破坏后, x x x y y y 不连通,那么我们称 z z z 为关于 x , y x,y x,y 的关键点。相应的,对于任意一对站点 x x x y y y,危险系数 D F ( x , y ) DF(x,y) DF(x,y) 就表示为这两点之间的关键点个数。

本题的任务是:已知网络结构,求两站点之间的危险系数。

输入格式

输入数据第一行包含 2 2 2 个整数 n ( 2 ≤ n ≤ 1000 ) n(2 \le n \le 1000) n(2n1000) m ( 0 ≤ m ≤ 2000 ) m(0 \le m \le 2000) m(0m2000),分别代表站点数,通道数。

接下来 m m m 行,每行两个整数 u , v ( 1 ≤ u , v ≤ n , u ≠ v ) u,v(1 \le u,v \le n,u\neq v) u,v(1u,vn,u=v) 代表一条通道。

最后 1 1 1 行,两个数 u , v u,v u,v,代表询问两点之间的危险系数 D F ( u , v ) DF(u,v) DF(u,v)

输出格式

一个整数,如果询问的两点不连通则输出 − 1 -1 1

样例 #1

样例输入 #1

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

样例输出 #1

2

提示

时限 1 秒, 64M。蓝桥杯 2013 年第四届国赛

思路:

依题意从x走到到y,有很多种走法但没关系,我们只需要分别记录所经过的各个点的次数,最后如果一个点所经过的次数等于从x走到y的不同走法数,则这个点为关键点,即每次从x走到y都需要经过这个点,即当这个点被敌人破坏后,x和 y不连通。至于储存从哪个点可以走到哪个点,我们可以用图的知识,此题为无向图。

#include<iostream>
#include<vector>

using namespace std;
vector <int> a[1000];	//数组a的每一个元素都是一个vector容器
int vis[1000]={0};		//用于标记某点是否已经走过
int cnt[1000]={0};		//记录走过某个点的次数
int ed,total=0;			//ed为终点,total表示走法数
int n,m;
void dfs(int now)		//深度优先搜索
{
	if(now==ed)			//确定搜索出口
	{
		total++;
		for(int i=1;i<=n;i++)
		{
			if(vis[i])cnt[i]++;	//若在这一次走法中i点走过则cnt【i】++
		}
		return;
	}
	for(int i=0;i<a[now].size();i++)
	{
		int next=a[now][i];	//走到下一个点
		if(vis[next]==0)	//判断下一个点是否走过
		{
			vis[next]=1;
			dfs(next);		//递归思想
			vis[next]=0;	//这一步很重要一定不能漏了!
		}
	}
}

int main()
{		
	cin>>n>>m;
	
	for(int i=0;i<m;i++)
	{
		int x,y;
		cin>>x>>y;
		a[x].push_back(y);		//注意这是无向图
		a[y].push_back(x);
	}
	
	int st;
	cin>>st>>ed;		//起点st,终点ed
	vis[st]=1;			//先标记起点已走过
	dfs(st);
	
	int ans=0;			//ans记录关键点的个数
	for(int i=1;i<=n;i++)
	{
		if(cnt[i]==total)ans++;
	}
	ans-=2;			//排除起点和终点这两个点
	cout<<ans;
	
	return 0;
}

图的遍历

题目描述

给出 N N N 个点, M M M 条边的有向图,对于每个点 v v v,求 A ( v ) A(v) A(v) 表示从点 v v v 出发,能到达的编号最大的点。

输入格式

1 1 1 2 2 2 个整数 N , M N,M N,M,表示点数和边数。

接下来 M M M 行,每行 2 2 2 个整数 U i , V i U_i,V_i Ui,Vi,表示边 ( U i , V i ) (U_i,V_i) (Ui,Vi)。点用 1 , 2 , … , N 1,2,\dots,N 1,2,,N 编号。

输出格式

一行 N N N 个整数 A ( 1 ) , A ( 2 ) , … , A ( N ) A(1),A(2),\dots,A(N) A(1),A(2),,A(N)

样例 #1

样例输入 #1

4 3
1 2
2 4
4 3

样例输出 #1

4 4 3 4

提示

  • 对于 60 % 60\% 60% 的数据, 1 ≤ N , M ≤ 1 0 3 1 \leq N,M \leq 10^3 1N,M103
  • 对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 1 0 5 1 \leq N,M \leq 10^5 1N,M105

思路:

此题依旧利用图的知识,不过和上一题不同,这题是有向图。这题的技巧性比较强,想到方法了就很简单,没想到的话可能就很难做出来了。本题的关键在于运用逆向思维,把边的方向反向储存,让数字更大的点去走到数字小的点,并依次传递A(v)的值。

#include<iostream>
#include<vector>

using namespace std;
const int N=100001;//本人在这里掉过坑,建议不要打擦边球,数组尽量开大一点
vector <int> a[N];	//储存有向图的点和边
int n,m;
int ma[N]={0};		//ma[j]表示某一点j能走到的最大的点
int vis[N]={0};
void dfs(int now)	//简单的深度搜索
{
	for(int i=0;i<a[now].size();i++)
	{
		int to=a[now][i];
		if(vis[to]==0)
		{
			vis[to]=1;
			ma[to]=ma[now];
			dfs(to);
		}
	}
}
int main()
{		
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int u,v;
		cin>>u>>v;
		a[v].push_back(u);		//逆向思维,反向储存边
	}
	
	for(int i=n;i>=1;i--)		//注意是从数字大的点开始遍历
	{
		if(vis[i]==0)			//判断点i是否已经走过
		{
			ma[i]=i;			//先初始化
			dfs(i);
			vis[i]=1;			//标记点i已经走过了
		}	
	}
	
	for(int i=1;i<n;i++)cout<<ma[i]<<' ';
	cout<<ma[n];
	return 0;
}

封锁阳光大学

题目描述

曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

阳光大学的校园是一张由 n n n 个点构成的无向图, n n n 个点之间由 m m m 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

输入格式

第一行两个正整数,表示节点数和边数。
接下来 m m m 行,每行两个整数 u , v u,v u,v,表示点 u u u 到点 v v v 之间有道路相连。

输出格式

仅一行如果河蟹无法封锁所有道路,则输出 Impossible,否则输出一个整数,表示最少需要多少只河蟹。

样例 #1

样例输入 #1

3 3
1 2
1 3
2 3

样例输出 #1

Impossible

样例 #2

样例输入 #2

3 2
1 2
2 3

样例输出 #2

1

提示

【数据规模】
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 4 1\le n \le 10^4 1n104 1 ≤ m ≤ 1 0 5 1\le m \le 10^5 1m105,保证没有重边。

思路:

本题参考了洛谷上KesdiaelKen大佬的题解,运用链式向前星的知识。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
struct Edge
{
    int t;
    int nexty;
}edge[200000];
int head[20000];
int cnt=0;//链式前向星
void add(int a,int b)//存边
{
    cnt++;
    edge[cnt].t=b;
    edge[cnt].nexty=head[a];
    head[a]=cnt;
}
bool used[20000]={0};//是否遍历过
int col[20000]={0};//每一个点的染色
int sum[2];//黑白两种染色各自的点数
bool dfs(int node,int color)//染色(返回false即impossible)
{
    if(used[node])//如果已被染过色
    {
        if(col[node]==color)return true;//如果仍是原来的颜色,即可行
        return false;//非原来的颜色,即产生了冲突,不可行
    }
    used[node]=true;//记录
    sum[col[node]=color]++;//这一种颜色的个数加1,且此点的颜色也记录下来
    bool tf=true;//是否可行
    for(int i=head[node];i!=0&&tf;i=edge[i].nexty)//遍历边
    {
        tf=tf&&dfs(edge[i].t,1-color);//是否可以继续染色
    }
    return tf;//返回是否完成染色
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int a,b;
    while(m--)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);//存的是有向边,所以存两次
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(used[i])continue;//如果此点已被包含为一个已经被遍历过的子图,则不需重复遍历
        sum[0]=sum[1]=0;//初始化
        if(!dfs(i,0))//如果不能染色
        {
            printf("Impossible");
            return 0;//直接跳出
        }
        ans+=min(sum[0],sum[1]);//加上小的一个
    }
    printf("%d",ans);//输出答案
    return 0;
}

Lily

百合花(Lily)是一种美丽的花。她通常一年只开一次花,所以如果你看到百合花盛开,它会非常珍贵。然而,她对猫有剧毒,所以你必须注意让好奇的猫远离可爱的百合花。

你有n个网格的土壤土地排成一行,从1到n,其中一些是百合花。我们不想伤害百合,也不想伤害猫。你可以在网格上放一些猫粮,但对于任何有猫粮的网格i,在区域[i−1,i+1]不得含有百合花。你喜欢猫和百合,所以你想最大限度地增加有猫粮的格子。

设计满足上述要求的计划。

输入格式:

有一个整数n(1≤n≤1000)表示网格的数量。

第二行包含仅由“L”和“.”组成的字符串R,表示有和没有百合花的格子。

输出格式:

输出包含一行,字符串R′仅由“L”、“”组成和“C”,其中“C”表示在满足上述要求的同时分配给R中空网格的猫粮。

输入样例:

在这里给出一组输入。例如:

5
…L…

输出样例:

在这里给出相应的输出。例如:

C.L.C

思路:

这题很简单,就不赘述了。

#include<iostream>
using namespace std;
int main()
{
	int n;
	string r;
	cin>>n>>r;
	
	for(int i=0;i<n;i++)
	{
		if(r[i]=='.'&&(i==0&&r[i+1]!='L'||i==n-1&&r[i-1]!='L'||r[i-1]!='L'&&r[i+1]!='L'))
		r[i]='C';
	}
	
	if(n==1&&r[0]=='.')cout<<'C';	
	else cout<<r;
	return 0;
}

a * b

给出两个不超过1000位的十六进制数a,b。
求a∗b的值

输入格式:

输入共两行,两个十六进制的数

输出格式:

输出一行,表示a∗b

输入样例:

在这里给出一组输入。例如:

1BF52
1D4B42

输出样例:

在这里给出相应的输出。例如:
332FCA5924

思路:

数字位数太大,考虑用数组储存每一位数,转化为10进制后用小学就学过的竖式乘法计算结果,最后转化为16进制即可。

#include<bits/stdc++.h>

using namespace std;
int main()
{   
    static int num[1001],num2[1001],num3[2333];
    char n[1001];
    char n2[1001];
    cin>>n>>n2;
    int l2=strlen(n2),l=strlen(n);
    if(l<l2)
    {
        swap(l,l2);
        swap(n,n2);
    } 
    for(int i=l;i>=1;i--)
    {
        if(n[i-1]>='0'&&n[i-1]<='9')
        {
            num[l-i+1]=n[i-1]-'0';
        }
        else
        {
            num[l-i+1]=n[i-1]-'A'+10;
        }
    }
    for(int i=1;i<=l;i++)
    {
        if(num[i]>15)
        {
            num[i+1]++;
            num[i]%=16;
            continue;
        }
        break;
    }
    for(int i=l2;i>=1;i--)
    {
        if(n2[i-1]>='0'&&n2[i-1]<='9')
        {
            num2[l2-i+1]=n2[i-1]-'0';
        }
        else
        {
            num2[l2-i+1]=n2[i-1]-'A'+10;
        }
    }
    for(int i=1;i<=l2;i++)
    {
        if(num2[i]>15)
        {
            num2[i+1]++;
            num2[i]%=16;
            continue;
        }
        break;
    }
    int flag=0;
    for(int i=1;i<=l2;i++)
    {
        for(int j=1;j<=l;j++)
        {
            flag=(num[j]*num2[i]+num3[j+i-1])/16;
            num3[j+i-1]=(num[j]*num2[i]+num3[j+i-1])%16;
            num3[j+i]+=flag;
        }
    }
    int flag2=2333;
    while(num3[flag2]==0)
    {flag2--;}
    for(int i=flag2;i>0;i--)
    {
        if(!(i==flag2&&num3[i]==0))
        {
            if(num3[i]>9)
            {
                cout<<(char)(num3[i]-10+'A');
            }
            else
            {
                cout<<num3[i];
            }
        }
    }
}

山头狙击战

题目描述

小明为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次小明携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。小明是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,小明会等待敌人靠近然后射击。
正当小明为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时小明无法将他击毙,那么我们可怜的小明就将牺牲在敌人的刺刀下。现在小明用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,小明最多只能用多少时间给枪装上一发子弹?
说明:假设一开始小明的枪中就有一发子弹,并且一旦确定一个装弹时间,小明始终会用这个时间完成子弹的装卸。希望你能帮助小明脱离险境。

输入格式

每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表小明的射程。
接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。

输出格式

每组输出数据仅有一个整数,代表小明的换弹时间(单位为秒)。

样例输入

6 100
236
120
120
120
120
120

样例输出

25

#include<bits/stdc++.h>

using namespace std;
int n,d;
bool ccc(int a,int b)
{
    return a>b;
}
int check(long long s[],long long  x)
{
    long long t=s[0]-d;
    if(t<0)
    {t=0;}
    t+=x;
    for(int i=1;i<n;i++)
    {
        if(s[i]-t>d)
        {t+=s[i]-t-d;}
        else if(s[i]-t<0)
        {return 0;}
        t+=x;
    }
    return 1;
}
int main()
{
    cin>>n>>d;
    long long s[n];
    for(int i=0;i<n;i++)
    {
        cin>>s[i];
    }   
    sort(s,s+n);
    long long l=1,r=1e8,mid,ans=0;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(check(s,mid))
        {
            l=mid+1;
            ans=max(mid,ans);
        }
        else
        {
            r=mid-1;
        }
    }
    cout<<ans;
}

Reversing Linked List

Given a constant K and a singly linked list L, you are supposed to reverse the links of every K elements on L. For example, given L being 1→2→3→4→5→6, if K=3, then you must output 3→2→1→6→5→4; if K=4, you must output 4→3→2→1→5→6.

Input Specification:

Each input file contains one test case. For each case, the first line contains the address of the first node, a positive N (≤10
5
) which is the total number of nodes, and a positive K (≤N) which is the length of the sublist to be reversed. The address of a node is a 5-digit nonnegative integer, and NULL is represented by -1.

Then N lines follow, each describes a node in the format:

Address Data Next
where Address is the position of the node, Data is an integer, and Next is the position of the next node.

Output Specification:

For each case, output the resulting ordered linked list. Each node occupies a line, and is printed in the same format as in the input.

Sample Input:
00100 6 4
00000 4 99999
00100 1 12309
68237 6 -1
33218 3 00000
99999 5 68237
12309 2 33218
Sample Output:
00000 4 33218
33218 3 12309
12309 2 00100
00100 1 99999
99999 5 68237
68237 6 -1

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+2;
int s,n,k;

struct tt
{
    int last,next=-2,id;
}t[maxn];
void exchange(int a)
{
    int c=a,d=0,i=a,k2;
    for(int j=1;j<=k;j++)
    {
        k2=t[i].last;
        swap(t[i].last,t[i].next);
        if(j==k)
        {d=i;}
         i=k2;
    }
    swap(t[c].last,t[d].next);
    if(t[d].next!=-1)
    {
        t[t[d].next].last=d;
    }
}
int main()
{   
    cin>>s>>n>>k;
    for(int i=0;i<n;i++)
    {
        tt ttt;
        int a;
        cin>>a>>ttt.id>>ttt.next;
        t[a].next=ttt.next;
        t[a].id=ttt.id;
    }
    t[s].last=-2;

    int js=0;
    for(int i=s;i>=0;i=t[i].next)
    {
        js++;
        if(t[i].next==-1)
        {break;}
    }
    n=js;
    js=0;
    int js2;
    for(int i=s;i>=0;i=t[i].next)
    {
        js++;
        if(js==n-n%k)
        {
            js2=i;
        }
        if(t[i].next==-1)
        {break;}
        t[t[i].next].last=i;
    }
    for(int q=1;q<=n/k;q++)
    {
        exchange(js2);
        if(t[js2].last!=-2)
        {
            t[t[js2].last].next=js2;
        }
        if(q==n/k)
        {
        }
        else
        {
            js2=t[js2].last;
        }
    }
    for(int i=js2;i>=0;i=t[i].next)
    {
        printf("%.5d %d ",i,t[i].id);
        if(t[i].next<0)
        {
            cout<<t[i].next<<endl;
        }
        else
        {
            printf("%.5d\n",t[i].next);
        }
    }
}

一元三次方程

给定一个形如ax3+bx2+cx+d=0的一元三次方程。

已知该方程有三个不同的实数根(根与根之差的绝对值≥10 −6),且根范围均在[p,q]之间,你需要解出这个方程的三个根。

输入格式:

第一行一个整数T(1≤T≤1000),表示有T组数据

接下来T行,每行6个实数,分别表示a,b,c,d,p,q

数据保证:−10 2≤p,q≤10 2
,且对于∀x∈[p,q],−106≤f(x)≤10 6

输出格式:

输出三个实数,表示方程的三个解。

你的答案可以以任意顺序输出。

一个答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过10 −6

输入样例:

在这里给出一组输入。例如:

1
1.000000 -5.000000 -4.000000 20.000000 -10.000000 10.000000

输出样例:

在这里给出相应的输出。例如:

-2.000000 2.000000 5.000000
提示1:
样例所给方程的图像如下:

(略)

提示2:

利用一元二次方程的求根公式。

思路:

利用二分搜索的知识,逐步使答案精确。

#include<bits/stdc++.h>
using namespace std;
double a,b,c,d,p,q;
double f(double x)
{
    return a*pow(x,3)+b*pow(x,2)+c*x+d;
}

int main()
{
    int n;
    double eps=1e-7;
    cin>>n;
    for(int k=0;k<n;k++)
    {
        int flag=1;
        cin>>a>>b>>c>>d>>p>>q;
        double x1=(-2*b-sqrt(4*b*b-12*a*c))/(6*a);
        double x2=(-2*b+sqrt(4*b*b-12*a*c))/(6*a);
        if(x1>x2)
        {swap(x1,x2);}
        if(f(x1)<0)
        {flag=-1;}
        double l,r,mid;
        for(int aa=1;aa<=3;aa++)
        {
            if(aa==1)
            {l=p,r=x1;}
            else if(aa==2)
            {l=x1,r=x2,flag=-flag;}
            else if(aa==3)
            {l=x2,r=q,flag=-flag;}
            while(r-l>=eps)
            {
                mid=(l+r)/2;
                if(f(mid)*flag<0)
                {
                    l=mid+eps;
                }
                else
                {
                    r=mid-eps;
                }
            }
            printf("%.6lf ",mid);
        }
        printf("\n");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值