题目一:
中文题省略
思路:
裸最小生成树
代码:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int G[110][110];
int low[110];
int vis[110];
int N,M;
int prim()
{
int res=0;
vis[1]=1;
low[1]=0;
for(int i = 2 ; i <= M ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= M ; i++)
{
int minc = inf;
int pos=0;
for(int i = 1 ; i <= M ; i++)
if(!vis[i] && low[i] < minc )
{
minc = low[i];
pos = i;
}
if(minc == inf)
return -1;
vis[pos]=1;
res += minc;
for(int i = 1 ; i <= M ; i++)
if(!vis[i] && low[i] > G[pos][i])
low[i] = G[pos][i];
}
return res;
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d" , &N,&M) && N)
{
memset(vis,0,sizeof(vis));
memset(G,0x1f,sizeof(G));
memset(low,0x1f,sizeof(low));
for(int i = 0 ; i < N ; i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
G[a][b] = G[b][a] = w;
}
int res = prim();
res == -1 ? puts("?") : printf("%d\n" , res);
}
return 0;
}
题目二
题意:
给你一个图的邻接矩阵,求最小生成树,输出的是树中的最大边
思路:
无脑模板题,矩阵都给了,直接改上题的代码
代码:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int G[550][550];
int low[550];
int vis[550];
int m,n;
int prim()
{
int res=-1;
vis[1]=1;
low[1]=0;
for(int i = 2 ; i <= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int minc = inf;
int pos=0;
for(int i = 1 ; i <= n ; i++)
if(!vis[i] && low[i] < minc )
{
minc = low[i];
pos = i;
}
vis[pos]=1;
res = max(res,minc);
for(int i = 1 ; i <= n ; i++)
if(!vis[i] && low[i] > G[pos][i])
low[i] = G[pos][i];
}
return res;
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
//memset(G,0x1f,sizeof(G));
memset(low,0x1f,sizeof(low));
scanf("%d",&n);
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
scanf("%d" , &G[i][j]);
int res = prim();
printf("%d\n" , res);
}
return 0;
}
题目三
题意:
和上一题几乎完全相同,求的是最小生成树的总权值
思路:
改上一题代码
代码:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int G[550][550];
int low[550];
int vis[550];
int m,n;
int prim()
{
int res=0;
vis[1]=1;
low[1]=0;
for(int i = 2 ; i <= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int minc = inf;
int pos=0;
for(int i = 1 ; i <= n ; i++)
if(!vis[i] && low[i] < minc )
{
minc = low[i];
pos = i;
}
vis[pos]=1;
res += minc;
for(int i = 1 ; i <= n ; i++)
if(!vis[i] && low[i] > G[pos][i])
low[i] = G[pos][i];
}
return res;
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d",&n) && n)
{
memset(vis,0,sizeof(vis));
//memset(G,0x1f,sizeof(G));
memset(low,0x1f,sizeof(low));
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
scanf("%d" , &G[i][j]);
int res = prim();
printf("%d\n" , res);
}
return 0;
}
题目四:
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int n;
struct edge
{
int s;
int e;
int w;
int state;
bool operator < (const edge& b)const
{
if(state == b.state)
return w < b.w;
return state > b.state;
}
}road[10000+100];
int set[110];
int Find(int x)
{
return set[x] != x ? (set[x] = Find(set[x])) : x;
}
int merge(int x , int y)
{
return set[Find(x)] = Find(y);
}
int kru()
{
int m = (n-1)*n/2;
for(int i = 0 ; i <= n ; i++)
set[i]=i;
sort(road,road+m);
int res=0;
int cnt=0;
for(int i = 0 ; i < m ; i++)
{
int u = Find(road[i].e);
int v = Find(road[i].s);
if(u != v)
{
cnt++;
res += road[i].state ? 0 : road[i].w;
merge(u,v);
}
if(cnt == n-1)
return res;
}
}
int main()
{
//freopen("Input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d" , &n) && n)
{
int cnt=0;
int t = (n-1)*n/2;
for(int i = 0 ; i < t ; i++)
scanf("%d%d%d%d",&road[i].s , &road[i].e , &road[i].w , &road[i].state);
printf("%d\n" , kru());
}
return 0;
}
题目五
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int n,m,k;
struct edge
{
int s;
int e;
int w;
bool operator < (const edge& b)const
{
return w < b.w;
}
}road[30000];
int f[550];
int Find(int x)
{
return f[x] != x ? (f[x] = Find(f[x])) : x;
}
int merge(int x,int y)
{
return ((x=Find(x)) != (y=Find(y))) && (f[x]=y);
}
int kru()
{
sort(road,road+m);
int res=0;
int cnt=0;
for(int i = 1 ; i <= n ; i++)
cnt += (f[i]!=i);
for(int i = 0 ; i < m ; i++)
{
int u = Find(road[i].e);
int v = Find(road[i].s);
if(u != v)
{
cnt++;
res += road[i].w;
merge(u,v);
}
if(cnt == n-1)
return res;
}
return -1;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d" , &t);
while(t--)
{
scanf("%d%d%d" , &n,&m,&k);
for(int i = 0 ; i <= n ; i++)
f[i]=i;
for(int i = 0; i < m ; i++)
scanf("%d%d%d",&road[i].s , &road[i].e , &road[i].w);
for(int i = 0; i < k ; i++)
{
int tt;
scanf("%d" , &tt);
if(tt > 0)
{
int cur;
scanf("%d" , &cur);
int ff = Find(cur);
for(int j = 1 ; j < tt ; j++)
{
scanf("%d" , &cur);
f[Find(cur)] = ff;
}
}
}
printf("%d\n" , kru());
}
return 0;
}
题目六
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int cnt;
int s,p;
struct edge
{
int s;
int e;
double w;
bool operator < (const edge& b)const
{
return w < b.w;
}
}e[125000+100];
struct point
{
double x;
double y;
}pp[550];
double dis(point a , point b)
{
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
int f[550];
int find( int x )
{
return (f[x] != x) ? (f[x] = find(f[x])) : x;
}
void merger(int a , int b)
{
int x = find(a);
int y = find(b);
if(x!=y)
f[x] = y;
}
double kru()
{
int cc=0;
double res=0;
for(int i = 0 ; i < cnt ; i++)
{
int u = find(e[i].s);
int v = find(e[i].e);
if( u != v )
{
merger(u , v);
res = e[i].w;
cc++;
}
if(cc == p-s)
return res;
}
return -1.0;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d" , &t);
while(t--)
{
scanf("%d%d",&s,&p);
cnt=0;
for(int i = 1 ; i <= p ; i++)
{
scanf("%lf%lf" , &pp[i].x , &pp[i].y);
for(int j = 1 ; j < i ; j++)
{
e[cnt].s = i;
e[cnt].e = j;
e[cnt].w = dis(pp[i],pp[j]);
cnt++;
}
}
sort(e,e+cnt);
for(int i = 0 ; i <= p ; i++)
f[i]=i;
printf("%.2lf\n" , kru());
}
return 0;
}
题目七
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int G[2010][2010];
int vis[2010];
int low[2010];
int n;
char s[2010][10];
int calc(char a[] , char b[])
{
int cnt=0;
for(int i = 0 ; i < 7 ; i++)
cnt += (a[i] != b[i]);
return cnt;
}
int prim()
{
int res=0;
vis[1]=1;
low[1]=0;
for(int i = 2 ; i <= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int minc = inf;
int pos = 0;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] < minc)
{
pos = j;
minc = low[j];
}
}
vis[pos]=1;
res+=minc;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] > G[pos][j])
low[j] = G[pos][j];
}
}
return res;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d",&n) && n)
{
memset(low,0x1f,sizeof(low));
memset(vis,0,sizeof(vis));
memset(G , 0x1f , sizeof(G));
for(int i = 1 ; i <= n ; i++)
{
scanf("%s",s[i]);
for(int j = 1 ; j < i ; j++)
{
G[i][j] = G[j][i] = calc(s[i],s[j]);
}
}
printf("The highest possible quality is 1/%d.\n" ,prim());
}
return 0;
}
题目八
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int n;
int p[650];
int G[650][650];
int low[650];
int vis[650];
int prime[2000100];
void init()
{
prime[0]=prime[1]=1;
prime[2]=0;
for(int i = 2 ; i <= 2000000 ; i++)
if(!prime[2])
{
for(int j = i*2 ; j <= 2000000; j+=i)
prime[j]=1;
}
}
int w(int a , int b)
{
if(!prime[a] || !prime[b] || !prime[a+b])
return min(min(a,b),abs(a-b));
return inf;
}
LL prim()
{
vis[1]=1;
low[1]=0;
LL res=0;
for(int i = 2 ; i<= n ; i++)
low[i] = G[1][i];
for(int i = 2 ; i <= n ; i++)
{
int minc = inf;
int pos=0;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] < minc)
{
pos=j;
minc = low[j];
}
}
vis[pos] = 1;
res+=minc;
if(minc == inf)
return -1;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j] > G[pos][j])
{
low[j] = G[pos][j];
}
}
}
return res;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d" , &t);
init();
while(t--)
{
memset(G,0x1f,sizeof(G));
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d" , p+i);
for(int j = 1 ; j < i ; j++)
G[i][j] = G[j][i] = w(p[i],p[j]);
}
cout << prim() << endl;
}
return 0;
}
题目九
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int fi[1000000];
int n,m;
struct edge
{
int s;
int e;
int w;
bool operator < (const edge& b)const
{
return w > b.w;
}
}e[100000+100];
void init()
{
int a=1,b=1,c=2;
fi[1]=1;
do
{
fi[c]=1;
a=b;
b=c;
}while((c = a+b) < 1000000);
}
int pos;
int f[100000+100];
int find(int x)
{
return (x == f[x]) ? x : f[x] = find(f[x]);
}
void meger(int x , int y)
{
x = find(x);
y = find(y);
if(x!=y)
f[x]=y;
}
int kru(int st , int dir)
{
for(int i = 0 ; i <= n ; i++)
f[i]=i;
int cnt=0;
int res=0;
for(int i = st ; i>0&&i<=m ; i+=dir)
{
int u = find(e[i].s);
int v = find(e[i].e);
if(u!=v)
{
cnt++;
res+=e[i].w;
meger(u,v);
}
if(cnt == n-1)
break;
}
if(cnt < n-1)
return -1;
return res;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d" , &t);
init();
for(int ka = 1 ; ka <= t ; ka++)
{
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= m ; i++)
scanf("%d%d%d",&e[i].s,&e[i].e,&e[i].w);
sort(e+1,e+m+1);
int r = kru(1,1);
printf("Case #%d: ",ka);
if(r == -1)
{
puts("No");
continue;
}
int l = kru(m,-1);
int ok=0;
for(int i = l; i <= r; i++)
if(fi[i])
{
ok=1;
break;
}
ok ? puts("Yes") : puts("No");
}
return 0;
}
题目十:
思路:
很有意思的一道题,刚开始我是直接遍历每一个顶点,以每一个顶点为起点prim,当记录的顶点数为m时跳出,更新最小值及相关项,结果wa了,后来想想有可能边权之和最小了但是点权之和不是最大的,而且这样会遗漏情况,直接以除式形式为权值又不好建树,所以改了方法:
由于数据量很小,所以我先dfs获取所有顶点数为m的集合,然后在这个集合中选点建树,由于一个集合中点权之和一定是个定值,所以只要考虑边权之和最小就可以了,而这就是标准的最小生成树。这样枚举完所有情况,也不会出现遗漏了。
代码如下:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "map"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
int m,n;
int node[20];
int G[20][20];
int vis[20];
double low[20];
vector out;
double ans;
void prim(vector cur)
{
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
vis[cur[0]]=1;
low[cur[0]]=0;
double fenzi=0 , fenmu=0;
for(int i = 0 ; i < cur.size() ; i++)
fenmu += node[cur[i]];
for(int i = 0 ; i < m ; i++)
low[cur[i]] = G[cur[0]][cur[i]];
for(int i = 1 ; i < m ; i++)
{
int minc = inf;
int pos = 0;
for(int j = 0 ; j < m ; j++)
{
if(!vis[cur[j]] && low[cur[j]] < minc)
{
minc = low[cur[j]];
pos = cur[j];
}
}
vis[pos] = 1;
fenzi += minc;
for(int j = 0 ; j < m ; j++)
if(!vis[cur[j]] && low[cur[j]] > G[pos][cur[j]])
low[cur[j]] = G[pos][cur[j]];
}
if(ans > fenzi/fenmu + 1e-8)
{
out = cur;
ans = fenzi/fenmu;
}
return ;
}
void dfs(vector cur , int st)
{
if(cur.size() == m)
{
prim(cur);
return;
}
for(int i = st+1 ; i <= n ; i++)
{
cur.push_back(i);
dfs(cur,i);
cur.pop_back();
}
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d" , &n,&m) && (m||n))
{
ans = inf;
for(int i = 1 ; i <= n ; i++)
scanf("%d" , node+i);
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
scanf("%d" , &G[i][j]);
vector zouqi;
dfs(zouqi,0);
sort(out.begin() , out.end());
for(int i = 0 ; i < out.size() ; i++)
{
if(i==0)
printf("%d" , out[i]);
else
printf(" %d" , out[i]);
}
puts("");
}
return 0;
}
题目十一:
题意:
给一个无向图,求一颗生成树,它的最大边减去最小边的值最小
思路:
暴力kruscal,具体看代码一目了然
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
struct edge
{
int s;
int e;
int w;
bool operator < (const edge& b)const
{
return w < b.w;
}
}e[10010];
int f[110];
int n,m;
int ans;
int find(int x)
{
return f[x] == x ? x : f[x] = find(f[x]);
}
void merger(int x , int y)
{
x = find(x);
y = find(y);
if(x != y)
f[x] = y;
}
void kru(int st)
{
for(int i = 0 ; i <= n ; i++)
f[i] = i;
int cnt=0;
int minc=inf;
int maxc = -1;
for(int i = st ; i < m ; i++)
{
int x = find(e[i].s);
int y = find(e[i].e);
if(x != y)
{
cnt++;
merger(x,y);
minc = min(minc , e[i].w);
maxc = max(maxc , e[i].w);
}
if(cnt == n-1)
{
ans = min(ans,maxc - minc);
return ;
}
}
}
int main()
{
//freopen("input.txt" , "r" , stdin);
while(~scanf("%d%d" , &n,&m) && (n||m))
{
ans = inf;
for(int i = 0 ; i < m ; i++)
scanf("%d%d%d" , &e[i].s,&e[i].e,&e[i].w);
sort(e,e+m);
for(int i = 0 ; i <= m-n+1 ; i++)
kru(i);
if(ans == inf)
ans = -1;
printf("%d\n" , ans);
}
return 0;
}
题目十二:
题意:
输入一行四个数的是一个球的三维坐标加半径,两个球能相互触碰到的话权值为0,否则就要用边去连接这两个球,边的长度是球面之间最小距离
思路:
无脑模板题不解释
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;
int n;
struct ball
{
double x,y,z;
double r;
}Ball[110];
double dis(ball a , ball b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
double g[110][110];
int vis[110];
double low[110];
double prim()
{
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
vis[1]=1;
low[1]=0;
double res=0;
for(int i = 1 ; i <= n ; i++)
low[i] = g[1][i];
for(int i = 2 ; i <= n ; i++)
{
double minc = inf;
int pos = 0;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] < minc)
{
minc = low[j];
pos = j;
}
vis[pos]=1;
res += minc;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] > g[pos][j])
low[j] = g[pos][j];
}
return res;
}
int main()
{
//freopen("input.txt" , "r" , stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d" , &n) && n)
{
memset(g,0x1f,sizeof(g));
for(int i = 1 ; i <= n ; i++)
{
scanf("%lf%lf%lf%lf" , &Ball[i].x , &Ball[i].y , &Ball[i].z , &Ball[i].r);
for(int j = 1; j < i ; j++)
{
double tmpd = dis(Ball[i] , Ball[j]);
g[i][j] = g[j][i] = tmpd > Ball[i].r + Ball[j].r ? tmpd - (Ball[i].r + Ball[j].r) : 0;
}
}
printf("%.3lf\n" , prim());
}
return 0;
}
题目十三:
徐福能够让一条路的消耗为0,称之为魔法路
秦始皇想让总权值最小,徐福想让魔法路连接的两个城市的总人口最大
于是为了折中,令A = 魔法路连接的两个城市的总人口,B = 总权值-魔法路的原权值(即这条路权值为0之后的总权值)
要求A/B最大
思路:
构造生成树无疑,为了使A/B尽可能大,在A能够达到所有组合的情况下,B要尽量小,所以构造的是最小生成树,总权值为quanzhi
然后枚举所有边,分两种情况:
1,这条边在树上,那么分母就是quanzhi - w;
2,这条边不在树上,那么加上这条边之后一定会出现一个环,这条加上的路反正会变成0我们不管,现在要关注的是这棵树已经不是树了,也就是说
多造了无用边,秦始皇哪有这么笨……所以我们要去掉原树上的一条边。假设加入边连通城市a和b,为了保证连通性,肯定是去掉a-b中的一条边,
仍旧是B最小原则,去掉的就是连接a,b的唯一通路的一条权值最大的边。综上,此情况下B = quanzhi - g[a][b]
为什么要进行步骤2?因为我们需要让A尽可能大,选择的城市可能不是在树上直接连通的
于是发现就是求最小生成树的步骤
代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "string"
#include "cmath"
#include "queue"
#include "cstdio"
#include "algorithm"
#include "cctype"
#include "vector"
#include "map"
#include "stack"
using namespace std;
typedef long long LL;
const int INF = 0x6fffffff;
const int inf = 522133279;
const LL llinf = 1e30;
const double eps = 1e-10;
int n;
struct City
{
int x;
int y;
int people;
}city[1010];
double g[1010][1010];
int vis[1010];
double low[1010];
int tree[1010][1010]; //tree[i][j] 表示i - j这条边是不是在树上,是为1
double maxedge[1010][1010]; //maxedge[i][j] 生成树上连接i-j的唯一路径中的最大边
int pre[1010]; //pre[i]表示i点之前的那一点,由构造顺序决定
double dis(City a , City b)
{
return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
double prim()
{
memset(maxedge, 0 , sizeof(maxedge));
memset(vis,0,sizeof(vis));
memset(low,0x1f,sizeof(low));
memset(tree,0,sizeof(tree));
double res=0;
vis[1]=1;
low[1]=0;
pre[1]=1;
for(int i = 2 ; i <= n ; i++)
low[i] = g[1][i],pre[i]=1;
for(int i = 2 ; i <= n ; i++)
{
double minc = inf;
int pos =0;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] < minc)
{
pos = j;
minc = low[j];
}
vis[pos]=1;
res += minc;
maxedge[pre[pos]][pos] = maxedge[pos][pre[pos]] = g[pos][pre[pos]]; //相邻两点只有这一种权值了
tree[pre[pos]][pos] = tree[pos][pre[pos]] = 1; //边被加入树中
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] > g[pos][j])
{
low[j] = g[pos][j];
pre[j] = pos; //在刷新j到生成树的最小距离的同时,也要刷新j的前驱结点
}
for(int j = 1 ; j <= n ; j++)
if(vis[j] && j != pos) //如果j在树中且不是新加入的点,刷新
//最大边权就是j到pos前驱这个区间中最大的权值和新加的边的权值的较大一个,这里有点dp的意思
//注意不要被最小生成树构造法则误导:low[i]是i点到当前生成树的最小距离,同样maxedge也是这么构造起来的,有可能最小的边不能加入生成树,因为没有关联的顶点
//所以不要简单地认为low[pos]一定是最大的边
//当然kruskal是严格按照边的大小来的
maxedge[j][pos] = maxedge[pos][j] = max(maxedge[j][pre[pos]] , low[pos]); //需要说明的是low[i] == maxedge[i][pre[i]],因为只有这条边,没得选择
}
return res;
}
int main()
{
int t;
scanf("%d" , &t);
while(t--)
{
memset(g,0,sizeof(g));
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d%d%d" , &city[i].x,&city[i].y,&city[i].people);
for(int j = 1 ; j < i ; j++)
g[i][j] = g[j][i]= dis(city[i],city[j]);
}
double quanzhi = prim();
double maxc = 0;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
{
if(i==j) continue;
if(!tree[i][j])
maxc = max(maxc , 1.0*(city[i].people+city[j].people) / (quanzhi - maxedge[i][j]));
else
maxc = max(maxc , 1.0*(city[i].people+city[j].people) / (quanzhi - g[i][j]));
}
printf("%.2lf\n" , maxc);
}
return 0;
}
题目十四:
题意:
给一个图让你判断这个图的最小生成树是否唯一
思路:
做过上一题之后这题就显得很水了,就是一个很裸的次小:
先构造一颗最小生成树,然后用不在树中的边替换某位一路径中的最大段,假设这两短长度相同,那么就找到了另一颗最小生成树
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
int n,m;
int g[110][110];
int low[110];
int vis[110];
int maxedge[110][110];
int tree[110][110];
int pre[110];
int prim()
{
memset(low,0x1f,sizeof(low));
memset(vis,0,sizeof(vis));
memset(maxedge,0,sizeof(maxedge));
memset(tree,0,sizeof(tree));
low[1]=0;
vis[1]=1;
pre[1]=1;
int res=0;
for(int i = 2 ; i <= n ; i++)
low[i] = g[1][i] , pre[i]=1;
for(int i = 2 ; i <= n ; i++)
{
int minc = inf;
int pos = 0;
for(int j = 1 ; j <= n ; j++)
if(!vis[j] && low[j] < minc)
{
minc = low[j];
pos = j;
}
vis[pos]=1;
res+=minc;
maxedge[pos][pre[pos]] = maxedge[pre[pos]][pos] = g[pos][pre[pos]];
tree[pos][pre[pos]] = tree[pre[pos]][pos] = 1;
for(int j = 1 ; j <= n ; j++)
{
if(!vis[j] && low[j]>g[pos][j])
low[j] = g[pos][j] , pre[j] = pos;
}
for(int j = 1 ; j <= n ; j++)
{
if(vis[j] && j!=pos)
maxedge[pos][j] = maxedge[j][pos] = max(maxedge[pre[pos]][j],low[pos]);
}
}
return res;
}
int main()
{
int t;
scanf("%d" , &t);
while(t--)
{
memset(g,0x1f,sizeof(g));
scanf("%d%d" , &n,&m);
for(int i = 0 ; i < m ; i++)
{
int a,b,c;
scanf("%d%d%d" , &a,&b,&c);
g[a][b] = g[b][a] = c;
}
int ok=1;
int quanzhi = prim();
for(int i = 1 ; i <= n ; i++)
{
for(int j = 1 ; j < i ; j++)
//这里用更简单的判断边是否在树上的方法,即通过pre判断i,j是否直接相连,这样就不用设tree数组了
if(/*!tree[i][j]*/i != pre[j] && j != pre[i] && g[i][j] == maxedge[i][j])
{
ok = 0;
break;
}
if(!ok)
break;
}
if(ok)
printf("%d\n" , quanzhi);
else
puts("Not Unique!");
}
return 0;
}
题目十五:
题意:
有一群人要消灭藏在迷宫里的外星人,这群人可以在开始或者找到外星人之后分成数波朝不同方向走,走的路径总和是各个队伍的路径和(当然中途分叉的队伍在分叉前算一次路径),求最小路径总和
思路:
这题要是不能分叉,那就是dfs的问题,但是能分叉的话,显然就是一个最小生成树的问题,难就难在建图,建图我用bfs
用一个数组aline记录字母的标号(id),即顶点编号,为避免重复,每一个顶点(x,y)的索引是(x * 行数 + y)
由于是最小生成树,那就不用管什么起点终点了。哪出发都一样
注意:
为什么说这题是神坑题......因为x y之后还可能出现若干空格!!!
wa了2次,然后看了poj的discuss把getchar()改成while(getchar()!='\n');就过了...................擦
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include "iostream"
#include "cstring"
#include "algorithm"
#include "cmath"
#include "cstdio"
#include "sstream"
#include "queue"
#include "vector"
#include "string"
#include "stack"
#include "cstdlib"
#include "deque"
#include "fstream"
#include "map"
#define eps 1e-14
using namespace std;
typedef long long LL;
const int MAXN = 1000000+100;
const int INF = 0x6fffffff;
const int inf = 522133279;
const long long LLinf = 1e30;
char maze[110][110];
int g[110][110];
int low[110];
int vis[110];
int dir[4][2] = {0,1,1,0,-1,0,0,-1};
int aline[3000];
struct node
{
int x;
int y;
int path;
node(){x=0,y=0,path=0;}
node(int _x , int _y , int _path)
{
x = _x;
y = _y;
path = _path;
}
};
int hang,lie;
int id;
int border(int x , int y)
{
return (x >= 0 && x < hang) && (y >= 0 && y < lie);
}
void bfs(node s)
{
int use[51][51];
memset(use,0,sizeof(use));
use[s.x][s.y]=1;
queue que;
while(!que.empty())
que.pop();
que.push(s);
node tmp,pre;
int sid = s.x*hang+s.y;
while(!que.empty())
{
pre = que.front();
que.pop();
for(int i = 0 ; i < 4 ; i++)
{
int x = pre.x + dir[i][0];
int y = pre.y + dir[i][1];
if(border(x,y) && !use[x][y] && maze[x][y] != '#')
{
use[x][y]=1;
tmp.x = x;
tmp.y = y;
tmp.path = pre.path+1;
que.push(tmp);
if(isalpha(maze[x][y]))
g[aline[sid]][aline[x*hang+y]] = tmp.path;
}
}
}
}
int prim()
{
memset(low,0x1f,sizeof(low));
memset(vis,0,sizeof(vis));
vis[0]=1;
low[0]=0;
int res=0;
for(int i = 1 ; i < id ; i++)
low[i] = g[0][i];
for(int i = 1 ; i < id ; i++)
{
int minc = inf;
int pos=0;
for(int j = 0 ; j < id ; j++)
{
if(!vis[j] && low[j] < minc)
{
minc = low[j];
pos = j;
}
}
vis[pos] = 1;
res += minc;
for(int j = 0 ; j < id ; j++)
{
if(!vis[j] && low[j] >g[pos][j])
low[j] = g[pos][j];
}
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
getchar();
while(t--)
{
memset(g,0x1f,sizeof(g));
memset(aline,0,sizeof(aline));
scanf("%d%d",&lie,&hang);
while(getchar()!='\n')
;
id=0;
for(int i = 0 ; i < hang ; i++)
{
gets(maze[i]);
for(int j = 0 ; maze[i][j] ; j++)
if(isalpha(maze[i][j]))
aline[i*hang+j] = id++;
}
for(int i = 0 ; i < hang ; i++)
for(int j = 0 ; j < lie ; j++)
if(isalpha(maze[i][j]))
bfs(node(i,j,0));
printf("%d\n" , prim());
}
return 0;
}