题目地址:HDU 3832
这个题的这种方法我无法给出证明。
我当时这个灵感出来的时候是想的是要想覆盖的点最少,那就要尽量反复利用这些点,然后要有两个之间是通过还有一个点间接连接的,这样会充分利用那些点。
然后就这样写了一次,一直WA。。然后中午睡觉的时候突然想到了有一种情况这样做是不正确的。那就是有个点作为中间点,与三个点相连的情况,这样的情况尽管也符合。可是会有反复边。。。
可是恰恰相反。。反复边应该越多越好。
。
那就充分利用了这些点。那么这个点应该怎么找呢?那就直接枚举好了。可是枚举每一个点都求一次最短路的话非常明显不科学。。
反正仅仅是利用到那三个点的最短距离,那就仅仅对这三个点分别求一次,那别的点到这三个点的最短路就都求出来了。
然后枚举全部点到三个点的距离之和,找出最小的。再用n减去最小值就是须要关闭的最大值了。
代码例如以下:
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #include <map> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; int head[300], cnt, vis[300]; int d[3][301]; struct node1 { int x, y, r; } dian[1000000]; struct node { int u, v, w, next; } edge[1000000]; void add(int u, int v, int w) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; } void spfa(int source, int x) { memset(d[x],INF,sizeof(d[x])); memset(vis,0,sizeof(vis)); d[x][source]=0; deque<int>q; q.push_back(source); while(!q.empty()) { int u=q.front(); q.pop_front(); vis[u]=0; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; if(d[x][v]>d[x][u]+edge[i].w) { d[x][v]=d[x][u]+edge[i].w; if(!vis[v]) { vis[v]=1; if(!q.empty()&&d[x][v]<d[x][q.front()]) { q.push_front(v); } else { q.push_back(v); } } } } } } int main() { int t, n, x, y, r, ans, i, j, min1; double z; scanf("%d",&t); while(t--) { scanf("%d",&n); memset(head,-1,sizeof(head)); cnt=0; for(i=1; i<=n; i++) { scanf("%d%d%d",&dian[i].x,&dian[i].y,&dian[i].r); } for(i=1; i<=n; i++) { for(j=1; j<i; j++) { z=sqrt((dian[i].x-dian[j].x)*1.0*(dian[i].x-dian[j].x)+(dian[i].y-dian[j].y)*1.0*(dian[i].y-dian[j].y)); if(z<=dian[i].r+dian[j].r) { add(i,j,1); add(j,i,1); } } } spfa(1,0); spfa(2,1); spfa(3,2); min1=INF; if(d[0][2]==INF||d[0][3]==INF) { printf("-1 "); continue ; } for(i=1;i<=n;i++) { if(d[0][i]!=INF&&d[1][i]!=INF&&d[2][i]!=INF) { if(min1>d[0][i]+d[1][i]+d[2][i]+1) { min1=d[0][i]+d[1][i]+d[2][i]+1; } } } printf("%d ",n-min1); } return 0; }