知识点:最短路问题, 最小生成树, 拓扑排序, 并查集等.
题目链接: 点击打开链接
一.Good Luck in CET-4 Everybody!(HDU1847)
这道题我一看是博弈论,一脸懵逼,说好的图论,,,,,,我是找规律过的反正.
百度下来叫巴什博弈, 就是三的倍数先手稳输.
#include <cstdio>
int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
if(n % 3){
printf("Kiki\n");
}
else{
printf("Cici\n");
}
}
return 0;
}
二.Highways(POJ1751)
最小生成树.Kruskal算法.(百度了一下,Prim也有人写, 但是可能超时)
我是套的牛客网讲课学长给的板子.
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
//MAXN是最大点数,MAXM是最大边数
const int MAXN=4000;//一开始小了RE了,一怒之下开这么大
const int MAXM=500000;
int F[MAXN];//并查集
struct Edge
{
int u,v,w;
} edge[MAXM];///存路
int tol;//边的总数
void addedge(int u,int v,int w)
{
edge[tol].u=u;
edge[tol].v=v;
edge[tol++].w=w;
}
bool cmp(Edge a,Edge b)
{
return a.w<b.w;
}
int find(int x)
{
if(F[x]==-1)return x;
else return F[x]=find(F[x]);//路径压缩
}
///用并查集来维护已经连起来的关系。
int Kruskal(int n)
{
memset(F,-1,sizeof(F));
sort(edge,edge+tol,cmp);///对路排序
int cnt=0;///cnt是计数已经选了的路
int ans=0;
for(int i=0; i<tol; i++)
{
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w;
int t1=find(u);
int t2=find(v);
if(t1!=t2)
{ //最大的一条边
ans=max(ans,w);
F[t1]=t2;
cnt++;
}
if(cnt==n-1)break;
}
if(cnt<n-1)return -1;
else return ans;
}
int main()
{
int n;
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d",&n);
memset(edge,0,sizeof(edge));
tol=0;
for (int i=0; i<n; i++)
{
int u = i, v, w;
for(int j = 0; j < n; j++)
{
v = j;
scanf("%d", &w);
if(w){///输入跳过0
addedge(u,v,w);
}
}
}
int ans=Kruskal(n);
printf("%d\n",ans);
}
return 0;
}
三.Connect the Cities(HDU3371)
我Kruskal 和 Prim 都写了, 都超时了.百度了一个980ms的代码, 数据至于卡这么死吗.
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 505
#define INF 10000000
bool vis[MAXN];
int lowc[MAXN];
int n, m, k;
int maps[MAXN][MAXN];
int prim()
{
int ans = 0;
memset(vis, false, sizeof vis);
vis[1] = true;
for(int i = 1; i <= n; i++)
lowc[i] = maps[1][i];
for(int i = 2; i <= n; i++){
int minc = INF;
int p = -1;
for(int j = 1; j <= n; j++)
if(!vis[j] && minc>lowc[j])
{
minc = lowc[j];
p = j;
}
if(minc >= INF)
return -1;
ans += minc;
vis[p] = true;
for(int j = 1; j <= n; j++){
if(!vis[j] && lowc[j] > maps[p][j])
lowc[j] = maps[p][j];
}
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &n, &m, &k);
memset(maps, 0x3f, sizeof maps);
for(int i = 1; i <= m; i++){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if(maps[u][v] > w)
maps[u][v] = maps[v][u] = w;
}
for(int i = 1; i <= k; i++){
int t, a[MAXN];
scanf("%d", &t);
for(int j = 1; j <= t; j++)
scanf("%d", &a[j]);
for(int j = 1; j <= t; j++)
for(int l = 1; l <= t; l++)
maps[a[j]][a[l]] = 0;
}
printf("%d\n", prim());
}
return 0;
}
/**
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=505;
const int Max=0x3f3f3f3f;
int map[maxn][maxn],low[maxn],visit[maxn];
int n,m,k;
void prim()
{
int i,j,pos,min,mst=0;
memset(visit,0,sizeof(visit));
pos=1;
visit[1]=1;
for(i=1;i<=n;i++)
low[i]=map[pos][i];
for(i=1;i<n;i++)
{
min=Max;
for(j=1;j<=n;j++)//更新min的值
{
if(!visit[j] && min>low[j])
{
min=low[j];
pos=j;
}
}
mst+=min;
if(mst>=Max) break;//说明这个图不连通
visit[pos]=j;
for(j=1;j<=n;j++)
{
if(!visit[j] && low[j]>map[pos][j])//更新low数组
low[j]=map[pos][j];
}
}
if(mst>=Max)
printf("-1\n");
else
printf("%d\n",mst);
}
int main()
{
int t,i,j,x;
int p,q,c;
int a[101];
scanf("%d",&t);
while(t--)
{
memset(map,Max,sizeof(map));
scanf("%d%d%d",&n,&m,&k);
for(i=0;i<m;i++)
{
scanf("%d%d%d",&p,&q,&c);
if(map[p][q]>c)//这里也要注意一下,题目没说有重边,但是数据里面有,没加这个就wa
map[p][q]=map[q][p]=c;
}
while(k--)//处理已连接的边,把权值赋0;
{
scanf("%d",&x);
for(i=1;i<=x;i++)
scanf("%d",&a[i]);
for(i=1;i<=x;i++)
for(j=1;j<=x;j++)
map[a[i]][a[j]]=0;
}
prim();
}
return 0;
}
*/
/**
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 505
#define MAXM 25005
int father[MAXN];
int rak[MAXN];
struct Edge{
int u, v, w;
}edge[MAXM];
int tol;
int cnt;
void addedge(int u, int v, int w)
{
edge[tol].u = u;
edge[tol].v = v;
edge[tol++].w = w;
}
bool cmp(Edge a, Edge b)
{
return a.w < b.w;
}
void init(int n)
{
for(int i = 0; i <= n; i++){
father[i] = i;
rak[i] = 0;
}
}
int findfa(int x)
{
if(father[x] = x) return x;
else return father[x] = findfa(father[x]);
}
void unite(int x, int y)
{
x = findfa(x);
y = findfa(y);
if(x != y)
cnt++;
if(rak[x] < rak[y])
father[x] = y;
else{
father[y] = x;
if(rak[x] == rak[y])
rak[x]++;
}
}
int Kruskal(int n)
{
sort(edge, edge + tol, cmp);
int ans = 0;
for(int i = 0; i < tol; i++){
int rot1 = findfa(edge[i].u);
int rot2 = findfa(edge[i].v);
if(rot1 != rot2){
ans += edge[i].w;
unite(rot1, rot2);
}
if(cnt == n - 1)
break;
}
if(cnt < n - 1) return -1;
else return ans;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
tol = 0;
for(int i = 0; i < m; i++){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
init(n);
cnt = 0;
for(int i = 0; i < k; i++){
int t, l;
scanf("%d%d", &t, &l);
for(int j = 1; j < t; j++){
int z;
scanf("%d", &z);
unite(l, z);
}
}
sort(edge, edge + m, cmp);
printf("%d\n", Kruskal(n));
}
return 0;
}
*/
/*
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 505
#define MAXM 25005
int father[MAXN];
int rak[MAXN];
struct Edge{
int u, v, w;
}edge[MAXM];
int tol;
int cnt;
void addedge(int u, int v, int w)
{
edge[tol].u = u;
edge[tol].v = v;
edge[tol++].w = w;
}
bool cmp(Edge a, Edge b)
{
return a.w < b.w;
}
void init(int n)
{
for(int i = 0; i <= n; i++){
father[i] = i;
rak[i] = 0;
}
}
int findfa(int x)
{
if(father[x] = x) return x;
else return father[x] = findfa(father[x]);
}
void unite(int x, int y)
{
x = findfa(x);
y = findfa(y);
if(x != y)
cnt++;
if(rak[x] < rak[y])
father[x] = y;
else{
father[y] = x;
if(rak[x] == rak[y])
rak[x]++;
}
}
int Kruskal(int n)
{
sort(edge, edge + tol, cmp);
int ans = 0;
for(int i = 0; i < tol; i++){
int rot1 = findfa(edge[i].u);
int rot2 = findfa(edge[i].v);
if(rot1 != rot2){
ans += edge[i].w;
unite(rot1, rot2);
}
if(cnt == n - 1)
break;
}
if(cnt < n - 1) return -1;
else return ans;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
tol = 0;
for(int i = 0; i < m; i++){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
init(n);
cnt = 0;
for(int i = 0; i < k; i++){
int t, l;
scanf("%d%d", &t, &l);
for(int j = 1; j < t; j++){
int z;
scanf("%d", &z);
unite(l, z);
}
}
sort(edge, edge + m, cmp);
printf("%d\n", Kruskal(n));
}
return 0;
}
*/
/*
1
6 4 3
1 4 2
2 6 1
2 3 5
3 4 33
2 1 2
2 1 3
3 4 5 6
*/
四.Legal or Not(HDU3342)
判断是否为DAG, 有dfs和拓扑排序两种写法,我查了个拓扑排序的代码学了一下.
#include <bits/stdc++.h>
using namespace std;
#define N 105
bool G[N][N];
int top[N];///入度
bool topsort(int n)
{
int cnt = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{
if(!top[j]){
top[j]--;
cnt++;
if(cnt == n)
return true;
for(int k = 0; k < n; k++){
if(G[j][k])
top[k]--;
}
}
}
return false;
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) != EOF && n)
{
memset(G, 0, sizeof G);
memset(top, 0, sizeof top);
for(int i = 1; i <= m; i++){
int x, y;
scanf("%d%d", &x, &y);
if(!G[x][y]){///防止重复输入导致入度有问题
G[x][y] = true;
top[y]++;
}
}
printf(topsort(n)?"YES\n":"NO\n");
}
return 0;
}
五.畅通工程续(HDU1874)
最短路问题, 我用Dijkstra写的,套的书上的板子.学习了用vector数组建立图的写法.
注意这是无向图,两个方向都要存.
这个哥们写了四种写法:点击打开链接
里面有SPFA,我没学过,就想着写完了后网上搜博客,更新下我的关于最短路的博客.
#include <bits/stdc++.h>
using namespace std;
#define INF 10000000
#define N 205
#define M 1005
struct edge{int to, cost;};
typedef pair<int, int> P;
vector<edge> G[M];
int d[M];
void dijkstra(int s)
{
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d + N, INF);
d[s] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top();que.pop();
int v = p.second;
if(d[v] < p.first)
continue;
for(int i = 0; i < G[v].size(); i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) != EOF)
{
for(int i = 0; i < N; i++)
G[i].clear();
for(int i = 0; i < m; i++){
edge temp;
int u;
scanf("%d%d%d", &u, &temp.to, &temp.cost);{
G[u].push_back(temp);
int uto = temp.to;
temp.to = u;
u = uto;
G[u].push_back(temp);
}
}
int s, e;
scanf("%d%d", &s, &e);
dijkstra(s);
if(d[e] == INF)
printf("-1\n");
else
printf("%d\n", d[e]);
}
return 0;
}
六.Eddy's picture(HDU1161)
最小生成树,因为是稠密图所以用Prim写的.(现在百度了一下,都是Prim)还是用的牛客网的板子.建图是我自己建的.
#include <bits/stdc++.h>
using namespace std;
typedef pair<double, double> P;
#define MAXN 105
#define INF 10000000
bool vis[MAXN];
double lowc[MAXN];
int n;
P pots[MAXN];
double maps[MAXN][MAXN];
double prim()
{
double ans = 0;
memset(vis, false, sizeof vis);
vis[1] = true;
for(int i = 1; i <= n; i++)
lowc[i] = maps[1][i];
for(int i = 2; i <= n; i++){
double minc = INF;
int p = -1;
for(int j = 1; j <= n; j++)
if(!vis[j] && minc > lowc[j])
{
minc = lowc[j];
p = j;
}
if(minc >= INF)
return -1;
ans += minc;
vis[p] = true;
for(int j = 1; j <= n; j++){
if(!vis[j] && lowc[j] > maps[p][j])
lowc[j] = maps[p][j];
}
}
return ans;
}
int main()
{
while(scanf("%d", &n) != EOF)
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
maps[i][j] = INF;
for(int i = 1; i <= n; i++){
double x, y;
scanf("%lf%lf", &x, &y);
pots[i].first = x;
pots[i].second = y;
for(int j = 1; j < i; j++){
double dx = fabs(x - pots[j].first);
double dy = fabs(y - pots[j].second);
maps[i][j] = maps[j][i] = sqrt(dx*dx + dy*dy);
}
}
printf("%.2lf\n", prim());
}
return 0;
}
七.Frogger(POJ2253)
这道题想求最短路中的最长一条路.
链接:点击打开链接
讲解什么的看人家的吧.
我就是这种有点变动就写不下去的菜鸡...
忽略注释掉的Prim........太蠢了
#include <bits/stdc++.h>
using namespace std;
#define N 202
double d[N][N];
typedef pair<int, int> P;
P p[N];
double dis(int i, int j)
{
double x = fabs(p[i].first - p[j].first);
double y = fabs(p[i].second - p[j].second);
return sqrt(x * x + y * y);
}
int main()
{
int n, Case = 0;
while(scanf("%d", &n) != EOF && n)
{
for(int i = 1; i <= n; i++)
scanf("%d%d", &p[i].first, &p[i].second);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= i; j++)
d[i][j] = d[j][i] = j == i?0:dis(i, j);
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], max(d[i][k], d[k][j]));
printf("Scenario #%d\n", ++Case);
printf("Frog Distance = %.3lf\n\n", d[1][2]);
}
return 0;
}
/*
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
#define N 202
typedef pair<int, int> P;
bool vis[N];
double lowc[N];
double maps[N][N];
P pots[N];
int n;
double prim()
{
double ans = 0;
fill(lowc, lowc + N, INF);
memset(vis, 0, sizeof vis);
vis[1] = true;
for(int i = 1; i <= n; i++)
lowc[i] = maps[1][i];
for(int i = 2; i <= n; i++){
int p = -1;
double minc = INF;
for(int j = 1; j <= n; j++){
if(!vis[j] && minc > lowc[j]){
p = j;
minc = lowc[j];
}
}
if(p == -1)
return -1;
ans = max(ans, minc);
vis[p] = true;
for(int j = 1; j <= n; j++){
if(!vis[j] && lowc[j] > maps[p][j])
lowc[j] = maps[p][j];
}
}
return ans;
}
int main()
{
int Case = 0;
while(scanf("%d", &n) != EOF && n)
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
maps[i][j] = i != j ?INF:0;
for(int i = 1; i <= n; i++){
scanf("%d%d", &pots[i].first, &pots[i].second);
for(int j = 1; j < i; j++){
double x = abs(pots[i].first - pots[j].first);
double y = abs(pots[i].second - pots[j].second);
maps[i][j] = maps[j][i] = sqrt(x*x+y*y);
}
}
printf("Scenario #%d\n", ++Case);
printf("Frog Distance = %.3lf\n\n", prim());
}
return 0;
}
*/
八.产生冠军(HDU2094)
这题是我自己做的(欧耶!), 拓扑排序的题目.
算法很简单,根本不用拓扑排序,直接看入度为0的点是不是只有一个就好了.
#include <bits/stdc++.h>
using namespace std;
#define N 1005
bool G[N][N];
int indeg[N];
int n, k;
string names[N];
/*
bool topsort()
{
int cnt = 0;
for(int i = 1; i <= k; i++)
for(int j = 1; j <= k; j++)
{
if(!indeg[j]){
indeg[j]--;
{cnt++;cout << i << ' ' << j << ' ' << indeg[j] << endl;}
if(cnt == k)
return true;
for(int l = 1; l <= k; l++){
if(G[j][l])
indeg[l]--;
}
}
}
return false;
}
*/
int main()
{
while(scanf("%d", &n) && n)
{
memset(G, 0, sizeof G);
memset(indeg, 0, sizeof indeg);
for(int j = 1; j <= 2 * n; j++)
names[j] = "";
k = 1;
for(int i = 1; i <= n; i++){
string a, b;
cin >> a >> b;
bool f1 = true, f2 = true;
int pa, pb;
for(int j = 1; j < k; j++){
if(names[j] == a){
pa = j;f1 = false;
}
if(names[j] == b){
pb = j;f2 = false;
}
}
if(f1){
names[k] = a;
pa = k;
k++;
}
if(f2){
names[k] = b;
pb = k;
k++;
}
if(!G[pa][pb]){
G[pa][pb] = true;
indeg[pb]++;
}
}
int cnt = 0;
for(int i = 1; i < k; i++)
if(!indeg[i])
cnt++;
printf(cnt == 1?"Yes\n":"No\n");
}
return 0;
}
九.Ubiquitous Religions(POJ2524)
并查集.每组合一个宗教数量就减一.
#include <bits/stdc++.h>
using namespace std;
#define N 50000
int F[N];
int cnt;
void intl(int n)
{
for(int i = 1; i <= n; i++)
F[i] = i;
cnt = n;
}
int find(int a)
{
if(F[a] == a)
return a;
else
return F[a] = find(F[a]);
}
void unite(int a, int b)
{
int x = find(a);
int y = find(b);
if(x == y)
return;
else{
F[y] = x;
cnt--;
}
}
int main()
{
int Case = 0;
int n, m;
while(scanf("%d%d", &n, &m) != EOF && n)
{
intl(n);
for(int k = 1; k <= m; k++){
int i, j;
scanf("%d%d", &i, &j);
//printf("%d\n", find(8));
unite(i, j);
}
printf("Case %d: %d\n", ++Case, cnt);
}
return 0;
}
十.最短路
题目真是取了个赤裸裸的名字.直接套Dijkstra板子就好了
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
#define N 102
#define M 1002
typedef pair<int, int> P;
struct edge{
int to, cost;
};
vector<edge> G[N];
int d[M];
int n, m;
int ans = 0;
void dijkstra()
{
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d + N, INF);
d[1] = 0;
que.push(P(0, 1));
while(!que.empty())
{
P p = que.top();que.pop();
int v = p.second;
if(d[v] < p.first)
continue;
for(int i = 0; i < G[v].size(); i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF && n)
{
for(int i = 1; i <= n; i++)
G[i].clear();
for(int i = 1; i <= m; i++){
edge e;
int u;
scanf("%d%d%d", &u, &e.to, &e.cost);
G[u].push_back(e);
int temp = u;
u = e.to;
e.to = temp;
G[u].push_back(e);
}
dijkstra();
printf("%d\n", d[n]);
}
return 0;
}