tip:对于证明是否应该用贪心,只需要简单的证明一下,为什么取这种比取其他的优就行了。
讲解及证明:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html
Description
The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessly, so the large road network is too expensive to maintain. The Council of Elders must choose to stop maintaining some roads. The map above on the left shows all the roads in use now and the cost in aacms per month to maintain them. Of course there needs to be some way to get between all the villages on maintained roads, even if the route is not as short as before. The Chief Elder would like to tell the Council of Elders what would be the smallest amount they could spend in aacms per month to maintain roads that would connect all the villages. The villages are labeled A through I in the maps above. The map on the right shows the roads that could be maintained most cheaply, for 216 aacms per month. Your task is to write a program that will solve such problems.
The input consists of one to 100 data sets, followed by a final line containing only 0. Each data set starts with a line containing only a number n, which is the number of villages, 1 < n < 27, and the villages are labeled with the first n letters of the alphabet, capitalized. Each data set is completed with n-1 lines that start with village labels in alphabetical order. There is no line for the last village. Each line for a village starts with the village label followed by a number, k, of roads from this village to villages with labels later in the alphabet. If k is greater than 0, the line continues with data for each of the k roads. The data for each road is the village label for the other end of the road followed by the monthly maintenance cost in aacms for the road. Maintenance costs will be positive integers less than 100. All data fields in the row are separated by single blanks. The road network will always allow travel between all the villages. The network will never have more than 75 roads. No village will have more than 15 roads going to other villages (before or after in the alphabet). In the sample input below, the first data set goes with the map above.
The output is one integer per line for each data set: the minimum cost in aacms per month to maintain a road system that connect all the villages. Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit.
Sample Input
9 A 2 B 12 I 25 B 3 C 10 H 40 I 8 C 2 D 18 G 55 D 1 E 44 E 2 F 60 G 38 F 0 G 1 H 35 H 1 I 35 3 A 2 B 10 C 40 B 1 C 20 0
Sample Output
216 30
做法一(对点操作):党组织之树
每次贪心选离已选点系统(党组织)最近的一个点加入进来
代码1:Prim算法(用最短路Dij优化)
#include <iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
#define inf 0x3f3f3f
int edge[36][36];
int N;
int dis[36];
bool vis[36];
void init()
{
int i,j;
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
{
edge[i][j]=inf;
}
}
}
/*前情提要:现在有一堆一起光屁股长大的人,每个人之间的关系有近有远,
(因为他们小时候为了抢玩具有的成了好兄弟,有的闹了矛盾,甚至老死不相往来)。
因为他们老是玩玩具,所以思想觉悟不高,一不小心走上了错误的道路。现在他们纠想要正自我,
走上正确的道路,于是积极入党想要学习。我们当然希望进党的同志离党组织的关系越近越好,
因为离党的关系越近,对党越忠心。所以在考虑谁入党的时候,都优先选离咱党组织关系近的那个*/
void prim()
{
int i,j,mink,mm;
int ans=0;
for(i=1;i<=N;i++)
{
dis[i]=inf;
/*dis[j]是到j点到党组织的关系,我们当然希望离党组织的关系越近越好。
刚开始大家都没入党,所以离党组织的关系无限远*/
vis[i]=false;//道路正确==是党员 道路错误==不是党员
//现在每个人都还是懵逼的,没有入党,所以所走的道路是错误的
}
dis[1]=0;//党主席离党的关系当然是0
for(i=1;i<=N;i++)//每个人都要全部入党,现在让第i个人入党
{
for(j=1,mm=inf;j<=N;j++)//我们现在选一个人推优入党,当然肯定选离咱党组织关系最近的那一个入党
{
if(dis[j]<=mm&&!vis[j])//每个人都在说:“看!我离党的关系最近,选我选我!”
{//当然,已经入党的就不能来瞎掺和了
mm=dis[j];
mink = j;
}
}
vis[mink]=true;//选出离党关系关系最近的那个人之后,他就从false变成了true,从此走上了正确的道路
ans+=dis[mink];
for(j=1;j<=N;j++)//进来一个,大家就都围上来想要拉近和党组织的关系,因为大家很踊跃
{
if(!vis[j] && edge[mink][j]<dis[j])//都已经是党员了就别来瞎掺和!
{/*如果大家与这个新钦定者在当年光屁股长大的时候的关系(即edge[mink][j])
比自己目前离党组织的距离(即dis)近,现在这个小伙伴入党了,
那么他可以通过抱这个小伙伴的大腿,让自己离党组织的关系更近一步,
开心地等着之后继续拉近和党的关系或者被钦定*/
dis[j] = edge[mink][j];
}
}
}
printf("%d\n",ans);
}
int main()
{
int n;
char st[5],ed[5];
int d;
while(scanf("%d",&N) && N!=0)
{
init();
for(int i=1;i<N;i++)
{
scanf("%s%d",st,&n);
for(int j=1;j<=n;j++)
{
scanf("%s%d",ed,&d);
edge[st[0]-'A'+1][ed[0]-'A'+1]=d;
edge[ed[0]-'A'+1][st[0]-'A'+1]=d;
}
}
prim();
}
return 0;
}//FROM WCF
代码2:暴力,无优化
#include <iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
#define inf 0x3f3f3f
int edge[36][36];
int N;
bool vis[36];
void init()
{
int i,j;
for(i=1; i<=N; i++)
{
vis[i]=false;
for(j=1; j<=N; j++)
{
edge[i][j]=inf;
}
}
}
void prim()
{
int i,j,k;
int ans=0;
vis[1] = true;
for(i=1; i<N; i++)
{
int mink = 1;
int mm= inf;
for(j=1; j<=N; j++)//求非党员与党组织之间的最近关系,j是非党员,k是党员
{
if(vis[j]==true) continue;//已经是党员了就别来瞎掺和了
int min = inf;
for(k=1; k<=N; k++)
{
if(vis[k]==false) continue;//对没一个党员遍历
min = edge[j][k]<min?edge[j][k]:min;
}
if(min<mm)
{
mm = min;
mink = j;
}
}
vis[mink]=true;
ans+= mm;
}
printf("%d\n",ans);
}
int main()
{
int n;
char st[5],ed[5];
int d;
while(scanf("%d",&N) && N!=0)
{
init();
for(int i=1; i<N; i++)
{
scanf("%s%d",st,&n);
for(int j=1; j<=n; j++)
{
scanf("%s%d",ed,&d);
edge[st[0]-'A'+1][ed[0]-'A'+1]=d;
edge[ed[0]-'A'+1][st[0]-'A'+1]=d;
}
}
prim();
}
return 0;
}//FROM YSF
做法二(对边操作):
Kruskal算法:对路径长度进行排序,贪心从小到大选择路径加入,如果成环则不能加入。注:非完全路径也适用于此方法
代码:
#include <iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
#define inf 0x3f3f3f
int N;
int pre[36];
int esum;
struct Edge
{
int st,ed,len;
}edge[1010];
bool cmp(Edge a,Edge b)
{
return a.len<b.len;
}
int find(int a)
{
int a1=a;
while(pre[a1]!=a1)
a1=pre[a1];
int a2=a;
while(pre[a2]!=a2)
{
int tem=pre[a2];
pre[a2]=a1;
a2=tem;
}
return a1;
}
void kruskal()
{
int i,x,y,ans=0;
for(i=1; i<=N; i++)
{
pre[i]=i;
}
for(i=1; i<=esum; i++)
{//不能成环才可以,并查集判断
x=find(edge[i].st);
y=find(edge[i].ed);
if(x!=y)
{
ans = ans+edge[i].len;
pre[x]=y;
}
}
printf("%d\n",ans);
}
int main()
{
int n;
char St[5],Ed[5];
int d;
while(scanf("%d",&N) && N!=0)
{
esum=0;
memset(edge,0,sizeof(edge));
for(int i=1; i<N; i++)
{
scanf("%s%d",St,&n);
for(int j=1; j<=n; j++)
{
scanf("%s%d",Ed,&d);
esum++;
edge[esum].st = St[0] -'A'+ 1;
edge[esum].ed = Ed[0] -'A'+ 1;
edge[esum].len = d;
}
}
sort(edge+1,edge+esum+1,cmp);
kruskal();
}
return 0;
}//FROM WCF
这里判断成环我想出这样的方法:如果加入一条边,就把这条边的两个端点book标记。之后判断是否成环的时候,如果两个端点都被标记了,那么就说明成环。这样就还能省下并查集的复杂度,由m*log2m变成m。
然而此法是错误的。
因为下面这种情况就不行了。
举一反三
应用1:
Description
We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
Input
Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
Output
Sample Input
3 0 990 692 990 0 179 692 179 0 1 1 2
Sample Output
179
思路:因为有的村庄之间的路已经修好了,所以我刚开始拿到题的时候心想:人家都帮我修好路了,可万一那条路是很长的怎么办呢?我还选它吗?
“你四不四傻啊!”人家都帮你修好了,那么就说明需要你修的长度为0啊,真是2b。于是就秒了。
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f
int edge[105][105];
int N;
int dis[105];
bool vis[105];
void init()
{
int i,j;
for(i=1; i<=N; i++)
{
for(j=1; j<=N; j++)
{
edge[i][j]=inf;
}
}
}
void prim()
{
int i,j,mink,mm;
int ans=0;
for(i=1; i<=N; i++)
{
dis[i]=inf;
vis[i]=false;
}
dis[1]=0;
for(i=1; i<=N; i++)
{
for(j=1,mm=inf; j<=N; j++)
{
if(dis[j]<=mm&&!vis[j])
{
mm=dis[j];
mink = j;
}
}
vis[mink]=true;
ans+=dis[mink];
for(j=1; j<=N; j++)
{
if(!vis[j] && edge[mink][j]<dis[j])
{
dis[j] = edge[mink][j];
}
}
}
printf("%d\n",ans);
}
int main()
{
int i,j,a,b,q;
while(~scanf("%d",&N))
{
init();
for(i=1; i<=N; i++)
{
for(j=1; j<=N; j++)
{
scanf("%d",&edge[i][j]);
}
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&a,&b);
edge[a][b]=edge[b][a]=0;
}
prim();
}
return 0;
}
应用2:
Description
Input
每组数据首先是一个整数C(C <= 100),代表小岛的个数,接下来是C组坐标,代表每个小岛的坐标,这些坐标都是 0 <= x, y <= 1000的整数。
Output
Sample Input
2 2 10 10 20 20 3 1 1 2 2 1000 1000
反思:这道题和前面的标准模板不一样,每次找党员的时候不见得找得到,所以flag没有值赋给他,故book[flag]=1不合法。
解决方法是:1、在前面先把flag赋为1,这样即使后面为赋值,flag也是1,而我们知道book[1]是等于1的,没有问题。
2、加个判断,如果发现tem值或者flag没变,即仍是inf,则直接返回或者continue。但是。。。。请告诉我!!按理说直接返回就不再执行剩下的while,时间应该比continue快才对,可是我直接返回是31ms,continue是15ms,你在逗我吗!!!!求大神解答
ac代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define MaxSize 105
#define inf 0x3f3f3f
int n;
double mp[MaxSize][MaxSize];
double dis[MaxSize];
int book[MaxSize];
struct node
{
int x, y;
} island[MaxSize];
void init()
{
for(int i=1; i<=n; i++)
{
dis[i]=inf;
book[i]=0;
for(int j=1; j<=n; j++)
{
mp[i][j]=inf;
}
}
}
double cal_dis(int x1, int y1, int x2, int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double prim()
{
int t=n-1,flag;
double sum=0;
dis[1]=0;
book[1]=1;
for(int i=1; i<=n; i++)
{
if(mp[1][i]<dis[i]&&book[i]==0)
dis[i]=mp[1][i];
}
while(t--)
{
//flag=1;
double tem=inf;
for(int i=2; i<=n; i++)
{
if(dis[i]<tem&&book[i]==0)
{
tem=dis[i];
flag=i;
}
}
if(tem==inf) return inf;
book[flag]=1;
sum+=tem;
for(int i=2; i<=n; i++)
{
if(mp[flag][i]<dis[i]&&book[i]==0)
dis[i]=mp[flag][i];
}
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=1; i<=n; i++)
{
scanf("%d%d",&island[i].x, &island[i].y);
for(int j=1; j<=i; j++)
{
double distance=cal_dis(island[j].x,island[j].y,island[i].x,island[i].y);
if(distance>=10&&distance<=1000)
mp[j][i]=mp[i][j]=distance;
else
mp[j][i]=mp[i][j]=inf;
}
}
double ans=prim();
if(ans<inf)
printf("%.1lf\n",100*ans);
else
printf("oh!\n");
}
return 0;
}//FROM CJZ
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define MaxSize 105
#define inf 0x3f3f3f
int n;
double mp[MaxSize][MaxSize];
double dis[MaxSize];
int book[MaxSize];
struct node
{
int x, y;
} island[MaxSize];
void init()
{
for(int i=1; i<=n; i++)
{
dis[i]=inf;
book[i]=0;
for(int j=1; j<=n; j++)
{
mp[i][j]=inf;
}
}
}
double cal_dis(int x1, int y1, int x2, int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double prim()
{
int t=n-1,flag;
double sum=0;
dis[1]=0;
book[1]=1;
for(int i=1; i<=n; i++)
{
if(mp[1][i]<dis[i]&&book[i]==0)
dis[i]=mp[1][i];
}
while(t--)
{
flag=1;
double tem=inf;
for(int i=2; i<=n; i++)
{
if(dis[i]<tem&&dis[i]>=10&&dis[i]<=1000&&book[i]==0)
{
tem=dis[i];
flag=i;
}
}
if(tem==inf) return inf;//31ms
//if(tem==inf) continue; //15ms
book[flag]=1;
sum+=tem;
for(int i=2; i<=n; i++)
{
if(mp[flag][i]<dis[i]&&book[i]==0)
dis[i]=mp[flag][i];
}
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=1; i<=n; i++)
{
scanf("%d%d",&island[i].x, &island[i].y);
for(int j=1; j<=i; j++)
{
mp[j][i]=mp[i][j]=cal_dis(island[j].x,island[j].y,island[i].x,island[i].y);
}
}
double ans=prim();
if(ans<inf)
printf("%.1lf\n",100*ans);
else
printf("oh!\n");
}
return 0;
}//FROM CJZ
为什么把长度判断放在prim里面就会wa?想不通,求看到这个问题的大神答疑,非常感谢
==============================================================================================================================
Description
Input
Output
Sample Input
1 2 4 0 100 0 300 0 600 150 750
Sample Output
212.13
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define MaxSize 505
#define inf 0x3f3f3f
int s,p,cnt;
int pre[MaxSize];
double cal_dis(int x1, int y1, int x2, int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
struct node1
{
int x, y;
} station[MaxSize];
struct node2
{
int x, y;
double len;
} mp[MaxSize*MaxSize];
bool cmp(node2 a, node2 b)
{
return a.len<b.len;
}
int find(int a)
{
int a1=a;
while(pre[a1]!=a1)
a1=pre[a1];
int a2=a;
while(pre[a2]!=a2)
{
int tem=pre[a2];
pre[a2]=a1;
a2=tem;
}
return a1;
}
void kruskal()
{
int flag=0;
for(int i=0; i<p; i++)
pre[i]=i;
for(int i=0; i<cnt; i++)
{
int x=find(mp[i].x);
int y=find(mp[i].y);
if(x!=y)
{
pre[x]=y;
flag++;
if(flag==p-s)
{
printf("%.2lf\n",mp[i].len);
break;
}
}
}
}
int main()
{
int N;
scanf("%d",&N);
while(N--)
{
cnt=0;
scanf("%d%d",&s,&p);
for(int i=0; i<p; i++)
{
scanf("%d%d",&station[i].x,&station[i].y);
for(int j=0; j<i; j++)
{
mp[cnt].len=cal_dis(station[i].x,station[i].y,station[j].x,station[j].y);
mp[cnt].x=i;
mp[cnt].y=j;
cnt++;
}
}
sort(mp,mp+cnt,cmp);
kruskal();
}
return 0;
}//FROM CJZ
这里有个东西很奇怪,输出用%.2lf交G++就wa,%.2lf交C++就ce,只有%.2f交G++才a,为什么为什么为什么
不过这种思路貌似有问题,但是没时间仔细看了,以后花时间来看。
http://www.lxway.com/809955254.htm
===========================================================================================================================
Description
Your task is to help the Borg (yes, really) by developing a program which helps the Borg to estimate the minimal cost of scanning a maze for the assimilation of aliens hiding in the maze, by moving in north, west, east, and south steps. The tricky thing is that the beginning of the search is conducted by a large group of over 100 individuals. Whenever an alien is assimilated, or at the beginning of the search, the group may split in two or more groups (but their consciousness is still collective.). The cost of searching a maze is definied as the total distance covered by all the groups involved in the search together. That is, if the original group walks five steps, then splits into two groups each walking three steps, the total distance is 11=5+3+3.
Input
Output
Sample Input
2 6 5 ##### #A#A## # # A# #S ## ##### 7 7 ##### #AAA### # A# # S ### # # #AAA### #####
Sample Output
8 11
这是一道出题思路很不错的题
代码(别人的):
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<algorithm>
#include <queue>
using namespace std;
#define inf 0x3f3f3f
#define MaxSize 105
int x[4]= {0,0,1,-1};
int y[4]= {1,-1,0,0};
int dis[MaxSize][MaxSize],low[MaxSize],node[MaxSize][MaxSize];
char map[MaxSize][MaxSize];
bool visit[MaxSize][MaxSize];
int n,m,node_sum;
struct Node
{
int x,y,dis;
};
void BFS(int i, int j)
{
memset(visit, false, sizeof(visit));
visit[i][j]=true;
Node b = {i, j, 0};
queue<Node> N;
N.push(b);
while(!N.empty())
{
Node front = N.front();
if(node[ front.x ][ front.y ])
dis[ node[b.x][b.y] ][ node[front.x][front.y] ] = front.dis;
//当时脑子卡了一下没理解,总觉得这里应该更新最小值。因为队列里每一个点都是一步一步走的,如果有一个点走到字母了,那它一定是第一个到的,而此时将其标记,其他点就不会走到这里了。
N.pop();
for(i=0; i<4; i++)
{
int xx = front.x+x[i];
int yy = front.y+y[i];
if(xx>=1&&xx<=n && yy>=1&&yy<=m)
if(map[xx][yy]!='#' && !visit[xx][yy])
{
visit[xx][yy] = true;
Node t = {xx, yy, front.dis+1};
N.push(t);
}
}
}
}
int Prim()
{
int s=1,i,count=1,prim_sum=0,t;
bool flag[MaxSize]= {false};
flag[s] = true;
for(i=1; i<=node_sum; i++)
low[i] = inf;
while(count < node_sum)
{
int min_dis = inf;
for(i=1; i<=node_sum; i++)
{
if(!flag[i] && low[i]>dis[s][i])
low[i] = dis[s][i];
if(!flag[i] && low[i]<min_dis)
{
min_dis = low[i];
t = i;
}
}
s = t;
count++;
flag[s] = true;
prim_sum += min_dis;
}
return prim_sum;
}
void init()
{
memset(map, '#', sizeof(map));
memset(dis, 0, sizeof(dis));
memset(node, 0,sizeof(node));
memset(dis, inf,sizeof(dis));
node_sum = 0;
}
int main()
{
int T,i,j;
char temp[MaxSize];
scanf("%d", &T);
while(T--)
{
init();
scanf("%d%d",&m,&n);
gets(temp);
for(i=1; i<=n; i++)
{
gets(map[i]);
for(j=1; j<=m; j++)
{
if(map[i][j]=='A' || map[i][j]=='S')
node[i][j]=++node_sum;
}
}
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
{
if(node[i][j])
BFS(i,j);
}
printf("%d\n", Prim());
}
return 0;
}