CSP图论题
csp201609-4 交通规划
题意:求点1到其他点的最短路
第一眼看上去是求最小生成树,但其实是求点1到其他点的最短路
只需要利用cost[]数组记录最短路上边的权值即可
但是对一个点v来说存在多条最短路的时候,cost[v]保存到v的边的最小的权值
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <stack>
#include <ctime>
using namespace std;
#define REP(i,a,b) for(int i = (a); i < (b); i++)
#define MS(a,b) memset(a,b,sizeof(a))
#define swap(a,b) a^=b^=a^=b
typedef pair<int,int> P;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e4 + 10;
const LL modd = 1e6;
int n, m, dis[MAXN], cost[MAXN];
vector<P> G[MAXN];
void spfa(int src){
MS(dis,INF);
MS(cost,INF);
priority_queue<P,vector<P>,greater<P> > que;
dis[src] = 0;
que.push(P(dis[src],src));
while(!que.empty()){
P t = que.top(); que.pop();
int u = t.second, d = t.first;
if(dis[u] < d)continue;
REP(i,0,G[u].size()){
int v = G[u][i].first, w = G[u][i].second;
if(dis[v] > d + w){
cost[v] = w;
dis[v] = d + w;
que.push(P(dis[v],v));
}else if(dis[v] == d + w){
cost[v] = min(cost[v],w);
que.push(P(dis[v],v));
}
}
}
return ;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int a, b, c;
cin >> n >> m;
REP(i,0,m){
cin >> a >> b >> c;
G[a].push_back(P(b,c));
G[b].push_back(P(a,c));
}
spfa(1);
LL ans = 0;
REP(i,2,n+1) ans += cost[i];
cout << ans << endl;
return 0;
}
csp201604-4 游戏
题意:求(1,1) 到 (n,m) 的最短路
只需要利用bfs来搜索权值为1的最短路
但要利用vis[x][y][t]来记录点(x,y)在时间t是否可以访问
#include <bits/stdc++.h>
using namespace std;
#define REP(i,a,b) for(int i = (a); i < (b); i++)
#define MS(a,b) memset(a,b,sizeof(a))
#define swap(a,b) a^=b^=a^=b
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define READ3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define READ4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define lson l,mid,ls
#define rson (mid+1),r,rs
typedef pair<int,int> P;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e2 + 10;
const long double PI = acos(-1.0);
const LL modd = 1e6;
int n, m, t, dir[][2] = {0,1,-1,0,1,0,0,-1};
int G[MAXN][MAXN];
bool vis[MAXN][MAXN][500];
int bfs(int ex, int ey){
queue<P> que;
que.push(P(0,1*1000+1));
vis[1][1][0] = true;
while(!que.empty()){
P t = que.front(); que.pop();
int x = t.second/1000, y = t.second%1000, step = t.first;
// if(vis[x][y][step]) continue; vis[x][y][step] = true;
if(x == ex && y == ey) return step;
REP(i,0,4){
int xx = x + dir[i][0], yy = y + dir[i][1], nextstep = step + 1;
int s = G[xx][yy]/1000, e = G[xx][yy]%1000;
if(xx < 1 || yy < 1 || xx > n || yy > m || G[xx][yy] != 0 && nextstep >= s && nextstep <= e || vis[xx][yy][nextstep]) continue;
que.push(P(nextstep,xx*1000+yy));
vis[xx][yy][nextstep] = true;
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
MS(G,0);
MS(vis,0);
cin >> n >> m >> t;
int a, b, s, e;
REP(i,0,t){
cin >> a >> b >> s >> e;
G[a][b] = s*1000+e;
}
cout << bfs(n,m) << endl;
return 0;
}
csp201512-4 送货
题意:求从点1出发到点n的欧拉路
只需要记录每个点的入度、出度,来判断是否存在以点1为起点的欧拉路
如果存在欧拉路输出路径,否则输出-1
输出路径,不要用dfs,用栈来模拟dfs,否则会爆栈
无向图
- 一个无向图 G 存在欧拉路径当且仅当无向图 G 是连通图, 且该图中有两个奇度顶点(度数为奇
数) 或者无奇度顶点。 - 当无向图 G 是包含两个奇度顶点的连通图时, G 的欧拉路径必定以这两个奇度顶点为端点。
- 一个无向图 G 存在欧拉回路当且仅当无向图 G 连通且不存在奇度顶点。
有向图
- 一个有向图 G 存在欧拉路径当且仅当 G 是连通的有向图, 且满足以下两个条件之一:
所有顶点的入度和出度相等;
有一个顶点的出度与入度之差为 1 , 一个顶点的出度与入度之差为 -1 , 其余顶
点的入度和出度相等。 - 当有向图 G 包含两个入度和出度不相同的顶点且有欧拉路径时, 欧拉路径必定以这两个入度出
度不相同的顶点为端点。 - 一个有向图 G 存在欧拉回路当且仅当图 G 是连通的有向图, 且所有顶点的入度和出度相等。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <iomanip>
#include <cmath>
#include <stack>
using namespace std;
#define REP(i,a,b) for(int i = (a); i < (b); i++)
#define MS(a,b) memset(a,b,sizeof(a))
#define READ(a) scanf("%d",&a)
#define READ2(a,b) scanf("%d%d",&a,&b)
#define READ3(a,b,c) scanf("%d%d%d",&a,&b,&c)
typedef pair<int,int> P;
typedef long long LL;
const int MAXN = 1e4 + 10;
const int MAXL = 1e3 + 10;
bool vis[MAXN][MAXN];
int n, m, f[MAXN], siz[MAXN];
void init(){MS(vis,0);REP(i,0,MAXN)f[i] = i, siz[i] = 1;}
int find(int x){return f[x] == x? x: f[x] = find(f[x]);}
void merge(int a, int b){ a=find(a);b=find(b);if(a!=b){f[a]=b;siz[b]+=siz[a];}}
set<int> G[MAXN];
stack<int> stk;
void dfs(int u){
stack<int> d;
d.push(u);
while(!d.empty()){
int u = d.top();
bool ok = false;
for(set<int>::iterator it = G[u].begin(); it != G[u].end(); it++){
int v = *it;
if(vis[u][v]) continue; vis[u][v] = vis[v][u] = true;
d.push(v);
ok = true;
break;
}
if(!ok) {
d.pop();
stk.push(u);
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
init();
bool ok = false;
cin >> n >> m;
int a, b;
REP(i,0,m){
cin >> a >> b;
G[a].insert(b);
G[b].insert(a);
merge(a,b);
}
if(siz[find(a)] == n){
int num = 0;
REP(i,1,n+1) if(int(G[i].size())%2 != 0)num++;
if(num == 2 && int(G[1].size())%2 != 0 || num == 0) {
ok = true;
dfs(1);
cout << 1;
stk.pop();
while(!stk.empty()) {
cout << ' ' << stk.top();
stk.pop();
}
cout << endl;
}
}
if(!ok) cout << -1 << endl;
return 0;
}
csp201509-4 高速公路
题意:给图G<V,E> 求其中两点双向可达的点队数
先利用tarjan求出强联通分量的个数scc,每个强联通分量中包含的点的个数siz[]
一个强连通分量v中,任意两点都互相可达,两点双向可达的点队数为 siz[v]*siz[v]/2
对每个强连通分量中的两点可达的点队数求和即可
[Code] losing
代码丢了,放个模板
void tarjan(int u,int fa) {
int cnt = 0;//统计u的孩子个数
low[u] = dfn[u] = ++idx;
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
if(v == fa) continue;
if(!dfn[v]){
tarjan(v,u);
++cnt;
low[u] = min(low[u],low[v]);
if((root != u && dfn[u] <= low[v]) || (root == u && cnt > 1))
//判断是否是割点
isap[u] = 1;
if(dfn[u] < low[v]) cutE[++numE]=Edge(u,v);
//判断是否是桥,视具体情况采用恰当的结构记录。
}
else low[u] = min(low[u],dfn[v]);//这里不用判断是否点v在栈中
}
}
csp201503-4 网络延时
题意:求树上任意两点的距离中最大的距离
这道题就是求树的直径,两次bfs即可
树的直径求法:
第一次任意选一个点进行dfs(bfs)找到离它最远的点,此点就是最长路的一个端点,再以此点进行dfs(bfs),找到离它最远的点,此点就是最长路的另一个端点,于是就找到了树的直径。
证明:略
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#define REP(i,a,b) for(int i = (a); i < (b); i++)
#define MS(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int,LL> P;
const int MAXN = 2e4 + 10;
const int INF = 0x3f3f3f3f;
vector<int> G[MAXN];
bool vis[MAXN];
int n, m;
LL bfs(int &src){
MS(vis,0);
LL ret = 0;
vis[src] = true;
queue<P> que;
que.push(P(0,src));
while(!que.empty()){
P t = que.front(); que.pop();
int u = t.second;
LL step = t.first;
ret = step;
src = u;
REP(i,0,G[u].size()){
int v = G[u][i];
if(!vis[v]) {
vis[v] = true;
que.push(P(step+1,v));
}
}
}
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in", "r", stdin);
freopen("out", "w", stdout);
#endif
int t;
cin >> n >> m;
REP(i, 2, n + 1) {
cin >> t;
G[t].push_back(i);
G[i].push_back(t);
}
REP(i, 1, m + 1) {
cin >> t;
G[t].push_back(i + n);
G[i + n].push_back(t);
}
int src = 1;
bfs(src);
cout << bfs(src) << endl;
return 0;
}
csp201412-4 最优灌溉
题意:求最小生成树
最小生成树摸板题
利用kruskal求即可
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <queue>
using namespace std;
#define REP(i,a,b) for(int i = (a); i < (b); ++i)
#define MS(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> P;
const int MAXN = 2e3 + 10;
const int MAXM = 1e5 + 10;
const int INF = 0x3f3f3f3f;
class edge{
public:
int u, v, cost;
bool operator < (const edge &b)const{
return cost < b.cost;
}
};
int n, m, f[MAXN];
edge dat[MAXM];
void init(){REP(i,0,MAXN)f[i] = i;}
int find(int x){return f[x] == x? x: f[x]=find(f[x]);}
void merge(int a, int b){ a=find(a),b=find(b);if(a != b)f[a] = b;}
LL kru(){
init();
LL ret = 0;
for(int i = 0, res = n; i < m && res > 1; i++){
int u = dat[i].u, v = dat[i].v, cost = dat[i].cost;
if(find(u) != find(v)){
merge(u,v);
ret += (LL)cost;
}
}
return ret;
}
int main(){
#ifdef QLOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m;
REP(i,0,m)
cin >> dat[i].u >> dat[i].v >> dat[i].cost;
sort(dat,dat+m);
cout << kru() << endl;
return 0;
}
csp201403-4 无线网络
题意: 求点1到点n的最少经过的点
先把n个坐标点中,两点距离小于r的点连边,转换成图G<V,E>
再把点权下方的边权,即dis[n]-1 d代表到 1-n中经过的点的数量
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <stack>
#include <ctime>
using namespace std;
#define REP(i,a,b) for(int i = (a); i < (b); i++)
#define MS(a,b) memset(a,b,sizeof(a))
#define READ2(a,b) scanf("%d%d",&a,&b)
#define READ3(a,b,c) scanf("%d%d%d",&a,&b,&c)
typedef pair<int,int> P;
typedef long long LL;
const int MAXN = 1e3 + 10;
const int INF = 0x3f3f3f3f;
const int MAXL = 1e3 + 10;
P dat[MAXN];
int n, m, k, r, dis[MAXN];
vector<int> G[MAXN];
class node{
public:
int first,second,cnt;
node(int a, int b, int c):first(a),second(b),cnt(c){}
bool operator < (const node &b)const{
return first > b.first;
}
};
void spfa(int src){
MS(dis,INF);
priority_queue<node> que;
dis[src] = 0;
que.push(node(0,src,0));
while(!que.empty()){
node t = que.top();que.pop();
int u = t.second, d = t.first, cnt = t.cnt;
if(dis[u] < d)continue;
REP(i,0,G[u].size()){
int v = G[u][i], w = 1;
if(dis[v] > d + w && cnt + (v >= n) <= k){
dis[v] = d + w;
que.push(node(dis[v],v,cnt+(v>=n)));
}
}
}
}
inline LL getDis(P a, P b){
return (LL)(a.first-b.first)*(a.first-b.first)+(LL)(a.second-b.second)*(a.second-b.second);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m >> k >> r;
int cnt = n + m;
REP(i,0,n) cin >> dat[i].first >> dat[i].second;
REP(i,n,cnt) cin >> dat[i].first >> dat[i].second;
REP(i,0,cnt)
REP(j,i+1,cnt)
if(getDis(dat[i],dat[j]) <= (LL)r*r){
G[i+1].push_back(j+1);
G[j+1].push_back(i+1);
}
spfa(1);
cout << dis[2] - 1 <<endl;
return 0;
}