ACM 图论作业整理

https://vjudge.net/contest/198762#problem

总体难度还行;

A、C、E、F、J稍微有点变形,剩下的都是模板题;

如果n  < 100 优先考虑Floyd;

题解

A:

注意到n最多100,直接枚举 “可以交易的地位” 的范围就行了;

做了很长时间,刚开始没读懂题意,后来考虑bfs,状态太多T掉了;

简单粗暴,直接暴力枚举就行了;

B:

裸的FLoyd

C:

好题,本质是个bfs,利用堆(dijkstra的贪心思想)来存状态,把金币超支的状态扔掉;

第一次到达n点时,即为n在金币数量限制下的最短路;

D:

把货币看作点建图;在无向图中求正环,只要回到原点金币增加输出YES;

E:

让两点之间经过的所有路径的最大权值最小;

最大值最小化,显然可以用二分最大值,然后dfs判断两点是否联通;时间复杂度为nlogn

考虑到n <= 200,因此考虑n ^ 3 的floy判断图的连通性即可,代码要简单很多;

F:

可以看到当一个节点的祖先的数量 + 孩子的数量 + 自己 = n时,这个点的排名可以确定;

因为祖先都能打败他,而孩子都能被他打败;所以统计数量就可以了;

可以直接dfs统计,但这样代码太长;

因此,n <= 100 的复杂度提醒我们,这又是一个floyd的变形;

直接判断图的连通性即可。

 

G、H:

裸的kruskal求最小生成树;

I:

并查集

J:

好题,分别求一遍最小生成树和最大生成树,如果在他们之间有斐波那契数,就可以;

证明:对于两棵树,每棵树的任取一点,两点相连形成新树,那么把一个黑边切掉,形成两个新树,再将白边连起来即可;

如果切掉每一条黑边都不能找到一个白边,则此时的树是最大生成树;

代码

A:
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN = 400001;
int fst[MAXN],nxt[MAXN],di[MAXN],a[MAXN];
ll dis[MAXN];
struct hh {ll d,num;};
struct sh {int f,t,c;}ma[MAXN << 1];
int tot,n,M,T,st,se;
ll ans,inf = 1e18;

priority_queue<hh>q;
bool operator < (hh a,hh b)
{
	return a.d > b.d;
}
void build(int f,int t,int c)
{
	ma[++ tot].f = f;
	ma[tot].t = t;
	ma[tot].c = c;
	nxt[tot] = fst[f];
	fst[f] = tot;
	return;
}

bool pd(int x,int y)
{
	return abs(x - y) <= M;
}

void Dijkstra(int sx)
{
	for(int i = 1;i <= n;i ++) dis[i] = inf;
 	dis[1] = 0;
	q.push((hh){0,1});
	while(!q.empty())
	{
		hh x = q.top();
		q.pop();
		for(int i = fst[x.num];i;i = nxt[i])
		{
			int v = ma[i].t;
			if(dis[v] > dis[x.num] + (ll)ma[i].c && pd(di[v],st) && pd(di[v],se))
			{
				dis[v] = dis[x.num] + (ll)ma[i].c;
				q.push((hh){dis[v],(ll)v});
			}
		}
	}
	return;
}

void solve()
{
	cin >> M >> n;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i] >> di[i] >> T;
		while(T --)
		{
			int t,c;
			cin >> t >> c;
			build(i,t,c);
		}
	}
	ans = a[1];
	int i = (di[1] - M >= 1 )? di[1] - M : 1;
	for(st = i;st <= di[1];st ++)
	{
		se = st + M;
		Dijkstra(1);
		for(int j = 1;j <= n;j ++) ans = min(ans,dis[j] + (ll)a[j]);
	}
	cout << ans << endl;
	return;
}

int main()
{
	solve();
	return 0;
}

C:

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
int fst[20001],n,m,C,f,t,d,c;
struct hh{int f,t,c,d,nxt;}ma[20002];
struct sh{int dis,D,num;}x;
priority_queue<sh>q;
bool operator < (sh a,sh b) {return a.dis == b.dis ? a.D > b.D : a.dis > b.dis;}
int bfs(){
	q.push((sh){0,0,1});
	while(!q.empty()){
		x = q.top(),q.pop();
		if(x.num == n) return x.dis;
		for(int i = fst[x.num];i;i = ma[i].nxt)
			if(x.D + ma[i].d <= C)
				q.push((sh){x.dis + ma[i].c,x.D + ma[i].d,ma[i].t});
	}
	return -1;
}
int main(){
	cin >> C >> n >> m;
	for(int i = 1;i <= m; i ++){
		scanf("%d%d%d%d",&f,&t,&c,&d);
		ma[i] = (hh){f,t,c,d,fst[f]},fst[f] = i;
	}
	printf("%d\n",bfs());
	return 0;
}

 J:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 500001;
int n,m,T,tt;
int fa[MAXN],f[MAXN];
struct hh{
	int f,t,c;
}ma[MAXN];

int find(int x){
	int r = x,t;
	while(r != fa[r]) r = fa[r];
	while(x != r) t = fa[x],fa[x] = r,x = t; 
	return r;
}
bool cmp(hh a,hh b){
	return a.c < b.c;
} 
void solve(){
	int ans1 = 0,ans2 = 0;
	cin >> n >> m;
	for(int i = 1;i <= m;i ++) scanf("%d%d%d",&ma[i].f,&ma[i].t,&ma[i].c);
	sort(ma + 1,ma + m + 1,cmp);
	for(int i = 1;i <= n;i ++) fa[i] = i; 
	for(int i = 1;i <= m;i ++){
		int fx = find(ma[i].f),fy = find(ma[i].t);
		if(fx != fy){
			fa[fx] = fy;
			ans1 += ma[i].c;
		}
	}
	int flag = 0;
	for(int i = 1;i <= n;i ++){
		if(fa[i] == i) flag ++;
		fa[i] = i;
	}
	for(int i = m;i >= 1;i --){
		int fx = find(ma[i].f),fy = find(ma[i].t);
		if(fx != fy){
			fa[fx] = fy;
			ans2 += ma[i].c;
		}
	}
	int pos = lower_bound(f,f + 31 ,ans1) - f;
	if(f[pos] <= ans2 && flag == 1) printf("Case #%d: Yes\n",++ tt);
	else printf("Case #%d: No\n",++ tt);
	return;
}


int main(){
	f[0] = f[1] = 1;
	for(int i = 2;i <= 30;i ++) f[i] = f[i - 1] + f[i - 2];
	cin >> T;
	while(T --) solve();
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值