按题目难度来写
1.租用游艇
比较明显的最短路 可以直接floyed
由于这次floyed是无相边的 所以我们将其枚举的时候变为单向(利用r(i,j)中i<j)
#include<iostream>
#include<algorithm>
using namespace std;
int dp[210][210];
int main()
{
int n;
cin >> n;
for(int i=1;i<=n-1;i++)
for (int j = i + 1; j <= n; j++)
{
cin >> dp[i][j];
}
for (int i = 1; i < n - 1; i++)
{
for (int j = i + 2; j <= n; j++)
{
for (int k = i + 1; k < j; k++)
{
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
}
}
}
cout << dp[1][n]<<endl;
}
2 邮递员送信
我们可以比较明确的发现一点:对于所有点我们既需要其从源点到达有需要其回到源点 对于前者我们可以直接一个dij从源点出发就好了 对于后面 因为所有点都要回到源点 所以我们干脆建立反向边再跑一次dij 两者相加就是答案
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define int long long
int n, m, s, head[10010], cnt;
struct sju
{
int u, v, dis;
}a[501000];
struct edge
{
int to, dis, next;
}e[510000];
int ans,dis[5001000], vis[10010];
void add(int u, int v, int dis)
{
e[++cnt].dis = dis;
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
struct node
{
int dis, pos;
bool operator<(const node&x)const
{
return x.dis < dis;
}
};
void dij()
{
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
std::priority_queue<node>q;
q.push({ 0,1 });
while (!q.empty())
{
int x = q.top().pos;
q.pop();
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].next)
{
int y = e[i].to;
if (!vis[y])
if (dis[y] > dis[x] + e[i].dis)
{
dis[y] = dis[x] + e[i].dis;
q.push({ dis[y],y });
}
}
}
for (int i = 1; i <= n; i++)
ans += dis[i];
}
signed main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
a[i].u = x, a[i].v = y, a[i].dis = z;
}
dij();
memset(head, 0, sizeof(head));
memset(e , 0,sizeof(e));
memset(vis, 0, sizeof(vis));
cnt = 0;
for (int i = 1; i <= m; i++)
add(a[i].v, a[i].u, a[i].dis);
dij();
cout << ans;
}
3
[USACO09NOV]Job Hunt S
我们可以发现到达每个城市都会挣钱 这是相当于一个点权,那如何将其与最短路联系呢 考虑当且仅当到达一个城市才会获得前 我们将达到这座城市的路边权赋值为D 对于飞机我们花费为-其费用+D 可以发现对于想要挣钱最多 得找到一点:从源点出发到这点的距离最大 -1的情况是有正环
改一下spfa就好了:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 1e5 + 5;
int head[maxn],ans,cnt, dist[maxn];
int sum[maxn];
bool vis[maxn];
int d, p, c, f, s;
struct edge
{
int to, dis, next;
}e[maxn<<1];
void add(int u, int v, int dis)
{
e[++cnt].dis = dis;
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
bool spfa(int u)
{
queue<int>q;
memset(vis, 0, sizeof(vis));
memset(sum, 0, sizeof(sum));
memset(dist, 0, sizeof(dist));
vis[u] = 1;
sum[u]++;
q.push(u);
dist[u] = d;
while (!q.empty())
{
int x = q.front();
q.pop();
vis[x] = 0;
for (int i = head[x]; i; i = e[i].next)
{
int y = e[i].to;
if (dist[y] < dist[x] + e[i].dis)
{
dist[y] = dist[x] + e[i].dis;
if (!vis[y])
{
if (++sum[y] >= c)
return 1;
vis[y] = 1;
q.push(y);
}
}
}
}
for (int i = 1; i <= c; i++)
ans = max(ans, dist[i]);
return 0;
}
int main()
{
cin >> d >> p >> c >> f >> s;
for (int i = 1; i <= p; i++)
{
int x, y;
cin >> x >> y;
add(x, y, d);
}
for (int i = 1; i <= f; i++)
{
int x, y, z;
cin >> x >> y >> z;
add(x, y, d - z);
}
if (spfa(s))
{
cout << -1<<endl;
}
else
{
cout << ans;
}
}
4:最短路计数
我们发现这题求得是最短路的条数,也就是方案数 一般和dp有着密不可分的关系 所以考虑dp
我们将所有边的边权赋值为1 跑一次最短路在维护最短路的同时将dp[i]变化
情况分为 1:dis[y]<dis[x]+1:代表此时更新最短路 所以dp[y]=dp[x];
2:dis[y]==dis[x]+1;代表此时与我们已找到的最短路相同 所以dp[y]+=dp[x];
最后输出dp即可
#include<iostream>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int mod = 100003;
int n, m;
struct edge
{
int to,next;
}e[2001000];
struct node
{
int pos, dis;
bool operator<(const node&x)const
{
return x.dis <dis;
}
};
int dis[1000100],head[1000100], cnt;
bool vis[1000100];
int ans[1000100];
void add(int u, int v)
{
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void dij()
{
std::priority_queue <node > q;
q.push({1,0});
memset(dis, 0x3f3f3f, sizeof(dis));
dis[1] = 0;
ans[1] = 1;
while (!q.empty())
{
int x = q.top().pos;
q.pop();
if (vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].next)
{
int y = e[i].to;
if (dis[y] > dis[x] + 1)
{
dis[y] = dis[x] + 1;//第一次经过时就是最短路
ans[y] = ans[x];
if (!vis[y])
{
q.push({ y,dis[y] });
}
}
else if(dis[y]==dis[x]+1)
{
ans[y]+=ans[x];
ans[y] %= mod;
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v,u);
}
dij();
for (int i = 1; i <= n; i++)
cout << ans[i] % mod<< endl;
}
P3371 P4779 P3385 均为模板题
9 灾后重建:他第一篇讲的很好
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
int n, m, q;
int d[101000];
int a[10010][10010];
void folyed(int k)
{
for (int i = 0; i <n; i++)
for (int j = 0; j <n; j++)
a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
}
signed main()
{
cin >> n >> m;
for (int i = 0; i <n; i++)
cin >> d[i];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
a[i][j] = 1e9;
for (int i = 1; i <= m; i++)
{
int a1, b, c;
cin >> a1 >> b >> c;
a[a1][b] = c;
a[b][a1] = c;
}
cin >> q;
int now = 0;
for (int i = 1; i <= q; i++)
{
int x, y, z;
cin >> x >> y >> z;
while (d[now] <= z&&now <=n-1)
{
folyed(now);
now++;
}
if (d[x]>z||d[y]>z||a[x][y] == 1e9 || a[y][x] ==1e9)
cout << -1 << endl;
else
cout << a[x][y]<<endl;
}
}
10 棋盘:bfs
假设无颜色为-1 有颜色为1/0
注意要点在于
我们如何处理变色后无法连续使用魔法:无法使用魔法的情况当且仅当我们此时本身color为-1 下一位也是-1 此时两者是不连通的 所以记录每次走到的变成的颜色和真实的颜色就好
这题我之前写的 就bfs 直接暴力跑 虽然可以加优先队列优化 主要是我这个没有标记vis 不管有剪纸
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
int n, m;
struct node
{
int x, y, cost, color;
};
int dx[5] = { 0,0,0,1,-1 };
int dy[5] = { 0,1,-1,0,0 };
int map[110][110];
int val[110][110];//记录走到x,y的最短花费
signed main()
{
cin >> n >> m;
memset(map, -1, sizeof(map));
memset(val, 0x3f3f3f, sizeof(val));
for (int i = 1; i <= m; i++)
{
int x, y, z;
cin >> x >> y >> z;
map[x][y] = z;
}
int ans = 1e9;
val[1][1] = 0;
queue<node>q;
q.push({ 1, 1, val[1][1], map[1][1] });
while (!q.empty())
{
node tmp = q.front();
if (q.front().cost > ans)
{
q.pop();
continue;
}
else
{
if (tmp.y == n && tmp.x == n)
{
ans = min(ans, tmp.cost);
q.pop();
continue;
}
else
{
q.pop();
for (int i = 1; i <= 4; i++)
{
int dx1 = tmp.x + dx[i], dy1 = tmp.y + dy[i];
if (dy1<1 || dx1<1 || dy1>n || dx1>n)
continue;
else
{
if (map[dx1][dy1] < 0 && map[tmp.x][tmp.y] >= 0)
{
if (val[dx1][dy1] > val[tmp.x][tmp.y] + 2)
{
val[dx1][dy1] = val[tmp.x][tmp.y] + 2;
q.push({ dx1,dy1,val[dx1][dy1],map[tmp.x][tmp.y] });
continue;
}
}
else if (map[dx1][dy1] >= 0 && map[tmp.x][tmp.y] >= 0)
{
if (map[dx1][dy1] == map[tmp.x][tmp.y])
{
if (val[dx1][dy1] > val[tmp.x][tmp.y])
{
val[dx1][dy1] = val[tmp.x][tmp.y];
q.push({ dx1,dy1,val[dx1][dy1],map[dx1][dy1] });
continue;
}
}
else
{
if (val[dx1][dy1] > val[tmp.x][tmp.y] + 1)
{
val[dx1][dy1] = val[tmp.x][tmp.y] + 1;
q.push({ dx1,dy1,val[dx1][dy1],map[dx1][dy1] });
continue;
}
}
}
else if (map[dx1][dy1] >= 0 && map[tmp.x][tmp.y] < 0)
{
if (map[dx1][dy1] == tmp.color)
{
if (val[dx1][dy1] > tmp.cost)
{
val[dx1][dy1] = tmp.cost;
q.push({ dx1,dy1,val[dx1][dy1],map[dx1][dy1] });
continue;
}
}
else
{
if (val[dx1][dy1] > tmp.cost + 1)
{
val[dx1][dy1] = tmp.cost + 1;
q.push({ dx1,dy1,val[dx1][dy1],map[dx1][dy1] });
continue;
}
}
}
}
}
}
}
}
if (ans == 1e9)
cout << -1;
else
cout << ans;
}
【模板】Johnson 全源最短路 建议直接当板子用
12:
这道题主要是在建图 我们假设一个2nx2m 的图 每个区域点的周围墙壁视作一个点 1代表连通 0 代表不连通
建图完毕后我们对于图中每一个点跑一次bfs 将其所在的区域标记 找到前两个答案
对于后两个 我们每一次将一个格子去除跑一次bfs找最大就好
细节比较多:
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define int long long
const int maxn = 110;
int a[110][110];//储存图
int p[55][55];
bool vis[110][110];
int ans = 0, num = 0;
int n, m;
int dx[4] = { 0,0,-1,1 };
int dy[4] = { 1, -1, 0, 0 };
int idx, idy;
void find(int x, int y)//{x,y}代表开头
{
queue<pair<int, int>>q;
int now = 1;
q.push({ x,y });//将起点储存
vis[x][y] = 1;
while (!q.empty())
{
int x = q.front().first, y = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int x1 = x + dx[i], y1 = y + dy[i];//代表转移的情况
if (x1 >= 0 && x1 <= 2 * n&&y1 >= 0 && y1 <= 2 * m&&a[x1][y1] == 0 && vis[x1][y1] == 0)
{
if ((x1 + y1) % 2 == 0)
now++;
q.push({ x1,y1 });
vis[x1][y1] = 1;
}
}
}
if (now > ans)
{
idx = x, idy = y;
ans = now;
}
else if(now==ans)
{
if (y < idy)
{
idx = x, idy = y;
}
else if(y==idy)
{
if (x > idx)
{
idx = x, idy = y;
}
}
}
}
signed main()
{
int x;
cin >> m >> n;
for (int i = 0; i <= 2 * n; i++)
for (int j = 0; j <= 2 * m; j++)
a[i][j] = 1;
for (int i = 1; i <= 2 * n; i += 2)
{
for (int j = 1; j <= 2 * m; j += 2)
{
cin >> x;
a[i][j] = 0;
if (x % 2 == 0)
{
a[i][j - 1] = 0;
}
x /= 2;
if (x % 2 == 0)
{
a[i - 1][j] = 0;
}
x /= 2;
if (x % 2 == 0)
{
a[i][j + 1] = 0;
}
x /= 2;
if (x % 2 == 0)
{
a[i + 1][j] = 0;
}
}
}
for (int i = 1; i <= 2 * n; i++)
{
for (int j = 1; j <= 2 * m; j++)
{
if (a[i][j] == 0 && vis[i][j] == 0)
{
num++;
find(i, j);
}
}
}
memset(vis, 0, sizeof vis);
cout << num << endl << ans<<endl;
ans = 0;
for (int i = 1; i <= 2 * n; i++)
{
for (int j = 1; j <= 2 * m; j++)
{
if (a[i][j] == 1&&i%2!=j%2)
{
a[i][j] = 0;
find(i, j);
a[i][j] = 1;
memset(vis, 0, sizeof vis);
}
}
}
cout << ans-1 << endl;
if(idx%2)
cout << (idx+1)/2 << ' ' << idy/2 <<" E"<< endl;
else
{
cout << idx / 2+1 << ' ' << (idy + 1) / 2 << " N" << endl;
}
}
骑士精神:
一道A*的模板题:
使用原因在于题目有明确要求在15步以内 所以这就代表 我们可以利用迭代加深来进行处理 :这类题的通解 A*的使用是为了经行剪枝经行优化
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define int long long
int d[6][6] = { {0,0,0,0,0,0},
{0,1,1,1,1,1},
{0,0,1,1,1,1},
{0,0,0,2,1,1},
{0,0,0,0,0,1},
{0,0,0,0,0,0} };//目标状态
int dx[8] = { 1,1,2,2,-1,-1,-2,-2 };//位移
int dy[8] = { 2,-2,1,-1,2,-2,1,-1 };
int map[10][10];//白色为0 黑色为1 空为2
string s[5];
bool falg = 0;//用来判断是否成功
int ju()
{
int cnt = 0;
for (int i = 1; i <= 5; i++)
{
for (int j = 1; j <= 5; j++)
{
if (map[i][j] != d[i][j])
cnt++;
}
}
return cnt;
}
void dfs(int x, int y, int f, int g)
{
int now=ju();//判断至少还要几步成功
if (now == 0)
{
falg = 1;
return;
}
if (now+g>f+1)
{
return;//代表不可能
}
for (int i = 0; i <= 7; i++)
{
int nowx = x + dx[i], nowy = y + dy[i];
if (nowx <= 0 || nowx > 5 || nowy <= 0 || nowy > 5)
continue;
swap(map[nowx][nowy], map[x][y]);
dfs(nowx, nowy, f, g + 1);
swap(map[nowx][nowy], map[x][y]);
}
}
void solve()
{
falg = 0;
int sx, sy;
char c;
for (int i = 1; i <= 5; i++)
{
for (int j = 1; j <= 5; j++)
{
cin >> c;
if (c== '*')
{
sx = i, sy = j;
map[i][j] = 2;
}
else if (c== '1')
{
map[i][j] = 1;
}
else
{
map[i][j] = 0;
}
}
}
//开始迭代加深:
for (int i = 1; i <= 15; i++) {
dfs(sx, sy, i, 0);
if (falg)
{
cout << i << endl;
return;
}
}
cout << -1 << endl;
return;
}
signed main()
{
int t;
cin >> t;
while (t--)
solve();
}
P1491 集合位置:
次短路题目:我们先跑一次dij 处理完最短路 后再次基础上每次删掉一条边 每删一次跑一次dij
求得此时最短就是次短路了
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int inf = 1e9;
const int maxn = 210;
const int maxm = 40040;
struct node
{
int pos;
double dis;
bool operator<(const node&x)const
{
return x.dis < dis;
}
};
struct edge
{
int to, next;
double dis;
}e[maxm];
double ans = inf;
int x[maxn], y[maxn];//记录点
int head[maxn], cnt,n,m;
double dis[maxn];
int prev1[maxn];//记录前驱
bool vis[maxn];
void add(int u, int v, double dis)
{
e[++cnt].dis = dis;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
double far(int x1, int y1, int x2, int y2)
{
return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}
void dij(int a,int b)//表示删掉ab之间的边
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++)
dis[i] =inf;
dis[1] = 0.0;
priority_queue<node>q;
q.push({ 1,0 });
while (!q.empty())
{
int u = q.top().pos;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
int y = e[i].to;
if ((u == a && y == b) || (u == b && y == a))
continue;
if (dis[y] > dis[u] + e[i].dis)
{
if (a == -1 && b == -1)
prev1[y] = u;//记录前驱
dis[y] = dis[u] + e[i].dis;
if (!vis[y])
{
q.push({ y,dis[y] });
}
}
}
}
}
int main()
{
double dis1 = 0.0;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> x[i] >> y[i];
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
dis1 = far(x[u], y[u], x[v], y[v]);
add(u, v, dis1);
add(v, u, dis1);
}
dij(-1, -1);//求最短路
int now = n;//最后一个点明确在n
while (prev1[now])
{
dij(prev1[now], now);
ans = min(ans, dis[n]);
now = prev1[now];
}
if (ans == inf)
cout << -1;
else
printf("%.2lf", ans);
}