【19.2.18】测试题解

题太水了懒得一个一个发

T1:

题意就是5000000个点,10000000条边,边权只有1或2,求最短路

我的做法:

先BB一句,要开始考试的时候L让我和gigo,gsy去楼下考,我说看一下题再决定,然后他说:你看,第一题是裸的最短路。又因为我懒得搬东西去楼下并且不想承受爆零/垫底的压力,于是就留在楼上。

然后做这道题的时候。嗯...L都说了是最短路,直接上SPFA,觉得可能会卡我于是用了SLF优化,又写上了DZYOの快读,测了下极限数据大概3 4秒,应该不会被卡了

正解:

把边权为2的边拆成两条边权为1的边进行BFS。

然后好像发现我比标算跑得快

代码:

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<18|1;
inline char nc()
{
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
template<class T>
inline void read(T &x)
{
	x=0; int f=1;
	static char ch=nc();
	while(!isdigit(ch)) ch=nc();
	while(isdigit(ch)) x=x*10+ch-'0',ch=nc();
	x*=f;
}
char xxx;
const int N=5000005;
const int M=10000005;
struct Edge
{
	int to,next;
	short val;
}edge[2*M];
int tot,first[N],n,m,t;
int dis[N];
bool inque[N];
char yyy;
inline void addedge(int x,int y,int z)
{
	tot++;
	edge[tot].to=y; edge[tot].next=first[x]; edge[tot].val=z; first[x]=tot;
}
inline void spfa_slf(int s,int t)
{
	memset(dis,0x3f,sizeof(dis));
	deque <int> q;
	dis[s]=0; inque[s]=1; q.push_back(s);
	while(!q.empty())
	{
		int now=q.front(); q.pop_front(); inque[now]=0;
		if(now==t)	break;
		for(register int u=first[now];u;u=edge[u].next)
		{
			int vis=edge[u].to;
			if(dis[now]+edge[u].val<dis[vis])
			{
				dis[vis]=dis[now]+edge[u].val;
				if(!inque[vis])
				{
					inque[vis]=1;
					if(!q.empty()&&dis[vis]<dis[q.front()]) q.push_front(vis);
					else q.push_back(vis);
				}
			}
		}
	}
	cout<<dis[t]<<endl;
}
int main()
{
	freopen("short.in","r",stdin);
	freopen("short.out","w",stdout);
	read(n),read(m),read(t);
	int u,v,w;
	for(register int i=1;i<=m;i++)
	{
		read(u),read(v),read(w);
		addedge(u,v,w);
		addedge(v,u,w);
	}
	spfa_slf(1,t);
	return 0;
}

T2:

题意:n 个字母,k条约束(x,y),表示x必须在y前面,问满足条件的全排列数

做法:直接全排列操过去

代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,a[15];
char ch[15];
vector<int> b[15];
map<char,int> ha;
bool check()	//验证a数组是否合法 
{
	for(int i=1;i<=n;i++)
		for(int j=0;j<b[a[i]].size();j++)
		{
			int tar=b[a[i]][j];
			int flag=0;
			for(int p=i+1;p<=n;p++)
				if(a[p]==tar) flag=1;
			if(!flag)	return false;
		}
	return true;
}
string s[100000];
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>ch[i],a[i]=i,ha[ch[i]]=i;
	char x,y;
	for(int i=1;i<=k;i++)
	{
		cin>>x>>y;
		b[ha[x]].push_back(ha[y]);
	}
	int cnt=0;
	do
	{
		if(check())
		{
			cnt++; 
			for(int i=1;i<=n;i++)	s[cnt]+=ch[a[i]];
		}
	}while(next_permutation(a+1,a+n+1));
	sort(s+1,s+cnt+1);

	for(int i=1;i<=cnt;i++)	cout<<s[i]<<endl;
	return 0;
}

T3:

一个无向图,可以删除一个点及其出去的边,问删去哪些点后剩下的点是一棵树

对于 40%的数据:n<=1000,m<=1000; 另外存在 10%的数据:m=n-1; 另外存在 20%的数据:m=n; 对于 100%
的数据:1e5

我的做法:
保险起见,分了三档,对于m=n-1,就是一颗树,其中叶子节点可以删,对于m=n,相当于找出基环树的环上的点,对于其他的,找出那些m-出度=n-2的点,然后判联通

我以为我可以过的,结果naive了。其实不需要每次O(n)判联通,只需要判断这个点是不是割点即可,CNM。

分段代码:

#include<bits/stdc++.h>
const int N=100005;
const int M=100005;
using namespace std;
template<class T>
inline void read(T &x)
{
	x=0; int f=1;
	static char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	x*=f;
}
int n,m,tot=1,first[N],father[N],kp,dfn[N],idx,f[N];
struct Edge
{
	int from,to,next,f;
}edge[2*M];
vector <int> p,ans,d;
inline void addedge(int x,int y)
{
	tot++;
	edge[tot].from=x; edge[tot].to=y; edge[tot].next=first[x]; first[x]=tot;
}
inline int getfather(int x)
{
	if(father[x]==x)	return x;
	return father[x]=getfather(father[x]);
}
inline bool check(int key)
{
	for(int i=1;i<=n;i++) father[i]=i;	
	for(int i=2;i<=tot;i++)
	{
		int u=edge[i].from,v=edge[i].to;
		if(edge[i].f==1)	continue;
		int fx=getfather(u),fy=getfather(v);
		if(fx==fy) continue;
		father[fx]=fy;
	}
	int last;
	if(key==1) last=getfather(2);
	else last=getfather(1);
	for(int i=1;i<=n;i++) 
	{
		if(i==key)	continue;
		int fa=getfather(i);
		if(fa!=last)	return false;
		last=fa;
	}
	return true;
}
void Solve1(int i)
{
	for(int u=first[i];u;u=edge[u].next)
	{
		edge[u].f=1;
		edge[u^1].f=1;
	}
	if(check(i)) ans.push_back(i);
	for(int u=first[i];u;u=edge[u].next)
	{
		edge[u].f=0;
		edge[u^1].f=0;
	}
}
void dfs(int now,int fa)
{
	int cnt=0;
	for(int u=first[now];u;u=edge[u].next)
	{
		int vis=edge[u].to;
		if(vis==fa)	continue;
		cnt++;
		dfs(vis,now);
	}
	if(cnt==0)	ans.push_back(now);
}
void dfs2(int now)
{
	dfn[now]=++idx;
	for(int u=first[now];u;u=edge[u].next)
	{
		int vis=edge[u].to;
		if(vis==f[now])	continue;
		if(dfn[vis])
		{
			if(dfn[vis]<dfn[now])	continue;
			ans.push_back(vis);
			for(;vis!=now;vis=f[vis])	ans.push_back(f[vis]);
		}
		else f[vis]=now,dfs2(vis); 
	}
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	read(n),read(m);
	for(int u,v,i=1;i<=m;i++)	
	{
		read(u),read(v);
		addedge(u,v);
		addedge(v,u);
	}
	for(int i=1;i<=n;i++)
	{
		int cnt=0;
		for(int u=first[i];u;u=edge[u].next) cnt++;
		if(m-cnt==n-2)	
			p.push_back(i);
	}
	if(m==n-1) //树 
	{
		for(int i=1;i<=n;i++)
		{
			int cnt=0;
			for(int u=first[i];u;u=edge[u].next) cnt++;
			if(cnt>1)	{dfs(i,0);break;}
		}
		cout<<ans.size()<<'\n';
		sort(ans.begin(),ans.end());
		for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
		return 0;
	}
	else if(n==m)	//基♂环树 
	{
		dfs2(1);
		for(int i=0;i<ans.size();i++)
		{
			int cnt=0;
			for(int u=first[ans[i]];u;u=edge[u].next) cnt++;
			if(cnt!=2)	ans.erase(ans.begin()+i),i--;
		}
		cout<<ans.size()<<'\n';
		sort(ans.begin(),ans.end());
		for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
		return 0;
	}
	else
	{
		for(int i=0;i<p.size();i++) Solve1(p[i]);
		cout<<ans.size()<<'\n';
		sort(ans.begin(),ans.end());
		for(int i=0;i<ans.size();i++) cout<<ans[i]<<" ";
		return 0;
	}
}

总结:

没什么总结,记忆犹新的就是最后几分钟对拍T2,结果发现出了点问题(没有输出),然后慌得一P,一度以为T2要爆零了,结果NM数据出错了。

下次还是去楼下找虐吧!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值