2019.03.23【NOIP提高组】模拟 A 组 spfa+bfs01图染色+?

17 篇文章 0 订阅
12 篇文章 0 订阅

0 World Tour

Cicasso是一个著名的雕塑家。
现在他想去城市之间旅游,他是一个聪明的人,所以从一个城市到另一个城市他只会走最短路。他想游览全国的风景,所以他想走的路的总长度尽量长,但是经费有限,他只能去四个城市,而且这四个城市不能重复(在途中经过的城市不计算,例如 ,他要去的四个城市有上标,[1, 5, 2, 4],这样是合法的)
注意,道路是单向路,并且距离都为1。

先求出多源最短路,然后记录从每个城市出发的最长路和次长路、到达这个城市的最长路;
枚举两个连通的城市,用它们的前驱最长路、次长路和后继最长路、次长路更新答案
时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <cstdio>
#include <cstring>

using namespace std;

int n,m,ans,z,x,l,b;
int a[3004][3004],ls[3004],y[5004],ne[5004];
int d[3][3004],s[3][3004];
int v[10000000][3];

void bfs(){
	int h=0,t=n;
	for (int i=1;i<=n;i++)
		v[i][0]=v[i][1]=i,a[i][i]=0;
	while (h<t){
		int e=v[++h][0],g=v[h][1],u=ls[v[h][1]];
		while (u){
			if (a[e][y[u]]==-1){
				a[e][y[u]]=a[e][g]+1;
				v[++t][0]=e,v[t][1]=y[u];
			}
			u=ne[u];
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	memset(a,-1,sizeof a);
	for (int j=1;j<=m;j++){
		int u,v;
		scanf("%d%d",&u,&v);
		ne[j]=ls[u],y[j]=v,ls[u]=j;
	}
	for (int i=1;i<=n;i++)	a[i][i]=0;
	bfs();
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		if ((i!=j)&&(a[i][j]!=-1)){ 
			if (a[i][j]>a[d[0][j]][j]) d[1][j]=d[0][j],d[0][j]=i; else
			if (a[i][j]>a[d[1][j]][j]) d[1][j]=i;
			if (a[i][j]>a[i][s[0][i]]) s[1][i]=s[0][i],s[0][i]=j; else
			if (a[i][j]>a[i][s[1][i]]) s[1][i]=j;
		}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		if ((i!=j)&&(a[i][j]!=-1))
			for (int c=0;c<=1;c++)
			if ((j!=d[c][i]))
				for (int e=0;e<=1;e++)
				if ((i!=s[e][j])){
					int u=d[c][i],v=s[e][j];
					if (a[u][i]+a[i][j]+a[j][v]>ans) 
						ans=a[u][i]+a[i][j]+a[j][v],z=u,x=i,l=j,b=v;
				}
	printf("%d %d %d %d",z,x,l,b);
}

1 Graph Coloring

现在你有一张无向图包含n个节点m条边。最初,每一条边都是蓝色或者红色。每一次你可以将一个节点连接的所有边变色(从红变蓝,蓝变红)。

找到一种步数最小的方案,使得所有边的颜色相同。

总体思想是分别检查将边变为蓝色,或者红色,然后取最优方案即可。
每一个点最多改变一次,因为改变两次等于没有变化。因此我们要将点分为两 个集合S和T,分别代表要改变的点和不需要改变的点。如果我们现在要将所有边变 为红色,假设u和v之间有一条红色的边。如果要维持这种颜色,u和v要属于同一个 集合(S或者T)。另一方面,如果这条边是蓝色,那么 u和v就必须在两个不同的集 合。
于是问题简化为了0-1图染色问题,我们可以通过DFS或者BFS解决。因为图 不一定连通,所以有一些细节要注意,比如可能有多个连通块,此时取每个连通块最优的方案才是答案。

#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=1e5+5;
int n,m,ans,ans1;
int tot,ls[N],ne[N*2],y[N*2],w[N*2];
int b[N];
queue <int> q;

int bfsred(){
	int ans2=0,t=0;
	for (int i=1;i<=n;i++)
	if (b[i]==-1){
		b[i]=t;int s1=0,s2=0;
		while (!q.empty()) q.pop();
		q.push(i);
		while (!q.empty()){
			int x=q.front(),u=ls[q.front()];q.pop();
			while (u){
				if (u==ls[i]) s1++;
				if (b[y[u]]==-1){
					if (w[u]==1) b[y[u]]=b[x];
					if (w[u]==0) b[y[u]]=b[x]^1;
					if (b[y[u]]==t) s1++; else s2++;
					q.push(y[u]);
				}else
				if ((b[y[u]]==b[x])&&(w[u]==0)) return -1;else
				if ((b[y[u]]!=b[x])&&(w[u]==1)) return -1;				
				u=ne[u];
			}
		}
		t+=2;
		ans2+=min(s1,s2);
	}
	return ans2;
}

int bfsblue(){
	int ans2=0,t=0;
	for (int i=1;i<=n;i++)
	if (b[i]==-1){
		b[i]=t;int s1=0,s2=0;
		while (!q.empty()) q.pop();
		q.push(i);
		while (!q.empty()){
			int x=q.front(),u=ls[q.front()];q.pop();
			while (u){		
				if (u==ls[i]) s1++;		
				if (b[y[u]]==-1){
					if (w[u]==0) b[y[u]]=b[x];
					if (w[u]==1) b[y[u]]=b[x]^1;
					if (b[y[u]]==t) s1++; else s2++;
					q.push(y[u]);
				}else
				if ((b[y[u]]==b[x])&&(w[u]==1)) return -1;else
				if ((b[y[u]]!=b[x])&&(w[u]==0)) return -1;				
				u=ne[u];
			}
		}
		t+=2;
		ans2+=min(s1,s2);
	}
	return ans2;
}

void set(int x,int r,int w1){
	ne[++tot]=ls[x];ls[x]=tot;y[tot]=r;w[tot]=w1;
	ne[++tot]=ls[r];ls[r]=tot;y[tot]=x;w[tot]=w1;
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++){
		char ch;int x,r; 
		scanf("%d%d",&x,&r);
		ch=getchar();ch=getchar();
		if (ch=='B') set(x,r,0); else set(x,r,1); 
	}
	memset(b,-1,sizeof b);
	ans=bfsred();
	memset(b,-1,sizeof b);
	ans1=bfsblue();
	if ((ans==-1)&&(ans1==-1)) printf("-1"); else
	if (ans==-1) printf("%d",ans1);else
	if (ans1==-1) printf("%d",ans);else
	printf("%d",min(ans,ans1));
}

2 LCS again

现在有一个长度为n的串S,其中每一个字母都是前m个小写字母
计算有多少个不同的长度为n的T(其中T也是由前m个小写字母组成),并且S与T的LCS为n-1
LCS就是同时存在于S和T的最长子序列

与我生命等价的浮木

学习,你是
与我生命等价的浮木

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值