9.19 寫題報告
阿新 • • 發佈:2020-09-19
9.19 做題報告
上午主要複習了一下圖論中比較基礎的最小生成樹,打了幾道水題(大霧)。
1.P1195 口袋的天空
模板題,只要聯通塊數變為 \(k\) 直接 \(break\) 就行。
Code
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,k,fa[10010],ans; struct node { int u,v,w; }e[100010]; inline int read() { int s = 0,w = 1; char ch; while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();} return s * w; } bool comp(node a,node b) { return a.w < b.w; } int find(int x) { if(fa[x] == x) return x; else return fa[x] = find(fa[x]); } int main() { n = read(); m = read(); k = read(); for(int i = 1; i <= m; i++) { e[i].u = read(); e[i].v = read(); e[i].w = read(); } sort(e+1,e+m+1,comp); for(int i = 1; i <= n; i++) fa[i] = i; int num = n; for(int i = 1; i <= m; i++)//krusal 板子題 { int x = e[i].u; int y = e[i].v; int fx = find(x), fy = find(y); if(fx == fy) continue; fa[fx] = fy; ans += e[i].w; if(--num == k) break; } if(num > k) printf("No Answer\n"); else printf("%d\n",ans); return 0; }
2.P1991 無線通訊網
也算是一道比較裸的題。
我們其實求的是當聯通塊數為 \(s\) 的時候,所加進去的邊權的最大值。
因為,我們可以給這 \(s\) 個聯通塊每個都發一個衛星電話,然後聯通塊之間的點可以用電話線連線。
剩下的就是最小生成樹的板子啦。
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int n,k,tot,fa[1010],x[1010],y[1010]; double ans; inline int read() { int s = 0,w = 1; char ch; while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();} return s * w; } struct node { int u,v; double w; }e[510*510]; int find(int x) { if(fa[x] == x) return x; else return fa[x] = find(fa[x]); } double dis(int i,int j) { return sqrt((x[i]-x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])); } bool comp(node a,node b) { return a.w < b.w; } int main() { k = read(); n = read(); for(int i = 1; i <= n; i++) { x[i] = read(); y[i] = read(); } for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { e[++tot].u = i; e[tot].v = j; e[tot].w = dis(i,j); } } for(int i = 1; i <= n; i++) fa[i] = i; sort(e+1,e+tot+1,comp); int num = n; for(int i = 1; i <= tot; i++)//krusal 板子 { int fx = find(e[i].u); int fy = find(e[i].v); if(fx == fy) continue; fa[fx] = fy; if(--num == k) { ans = e[i].w; break; } } printf("%.2lf",ans); return 0; }
3.P1265 公路修建
這題屬實把我繞蒙了,繞來繞去就是求個最小生成樹的板子題。
然後自信的打了個 \(krusal\) 的板子結果 $TLE $, 又 \(RE\) ,看了看題解才明白,原來這題是不能用 \(krusal\) 的,因為邊的數量太多,
我們陣列存不下,那麼只能老老實實的用 \(prime\) 了。
一個小優化就是: 我們不必先把所有的距離都求出來,那樣陣列開不下,我們只需要在用到他的時候再算就可以了。
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; const double inf = 2147483647.0; int n,x[5010],y[5010]; double dis[5010],ans; bool vis[5010]; inline int read() { int s = 0,w = 1; char ch; while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();} return s * w; } double d(int i,int j) { return (double) sqrt((double) (x[i]-x[j]) * (x[i] - x[j]) + (double) (y[i] - y[j]) * (y[i] - y[j]));//一定要轉成double 型別的 } int main() { n = read(); for(int i = 1; i <= n; i++) { x[i] = read(); y[i] = read(); } for(int i = 1; i <= n; i++) dis[i] = inf; dis[1] = 0; for(int i = 1; i <= n-1; i++) { int x = 0; for(int j = 1; j <= n; j++)//找當前距離最小的點 { if(!vis[j] && (x == 0 || dis[x] >= dis[j])) x = j; } vis[x] = 1; for(int j = 1; j <= n; j++) //更新一下其他點的距離 { if(!vis[j])dis[j] = min(dis[j], d(x,j)); } } for(int i = 1; i <= n; i++) ans += dis[i]; printf("%.2lf",ans); return 0; }
4.P1340 獸徑管理
今天上午做的題裡面唯一一道比較有思維含量的題。
首先,我們的暴力做法就是對每個都重新跑一邊最小生成樹,那樣肯定會 \(T\) 飛的。
我們其實並不需要對每次都跑一遍生成樹,當我們這次要刪的邊不在最小生成樹裡面的話,那麼刪了這條邊對答案也是沒有影響的。
因此,我們可以倒著從後往前處理,對在生成樹上的邊標記一下,如果刪的這條邊在生成樹上,就暴力重新跑一邊生成樹。
反之答案不變。
一個可以優化的地方,就是當 \(m < n-1\) 的時候,我們直接輸出 \(-1\) 就可以,因為這幾條邊是構不成一棵樹的。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n,m,tmp;
int fa[N],ans[N];
struct node
{
int u,v,w;
int tag,vis,id;
}e[N];
inline int read()
{
int s = 0,w = 1; char ch;
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
bool comp(node a,node b)
{
return a.w < b.w;
}
int find(int x)
{
if(fa[x] == x) return x;
else return fa[x] = find(fa[x]);
}
void krusal()
{
tmp = 0;
sort(e+1,e+m+1,comp);
for(int i = 1; i <= n; i++) fa[i] = i;
int num = n;
for(int i = 1; i <= m; i++) e[i].vis = 0;
for(int i = 1; i <= m; i++)
{
int fx = find(e[i].u), fy = find(e[i].v);
if(fx == fy || e[i].tag == 1) continue;
e[i].vis = 1;
fa[fx] = fy;
tmp += e[i].w;
if(--num == 1) return;
}
if(num != 1) tmp = -1;
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= m; i++)
{
e[i].u = read();
e[i].v = read();
e[i].w = read();
e[i].id = i;
}
krusal(); ans[m] = tmp;
for(int i = m-1; i >= 1; i--)
{
if(i < n-1)//少於 n-1 條邊構不成一棵樹
{
ans[i] = -1;
continue;
}
for(int j = 1; j <= m; j++)
{
if(e[j].id == i+1)//找到要刪除的那一條邊
{
e[j].tag = 1;
if(e[j].vis == 1) krusal();//如果這條邊在最小生成樹上,就需要在重新跑一邊最小生成樹
break;
}
}
ans[i] = tmp;
}
for(int i = 1; i <= m; i++) printf("%d\n",ans[i]);
return 0;
}