编程之美初赛第二场 攻城略地(贪心,图论)

来源:互联网 发布:人工智能权威杂志 编辑:程序博客网 时间:2024/05/01 20:26

题意:A、B两国间发生战争。已知A国共有n个城市(编号1, 2, …, n),城市间有一些道路相连。每座城市的防御力为w,直接攻下该城的代价是w。若该城市的相邻城市(有道路连接)中有一个已被占领,则攻下该城市的代价为0。除了占领城市,B国还要摧毁A国的交通系统,因而他们需要破坏至少k条道路。由于道路损毁,攻下所有城市的代价相应会增加。假设B国可以任意选择要摧毁的道路,那么攻下所有城市的最小代价是多少?

输入:第一行一个整数T,表示数据组数,以下是T组数据。每组数据第一行包含3个整数n, m, k。第二行是n个整数,分别表示占领城市1, 2, …, n的代价w。接下来m行每行两个数i, j,表示城市i与城市j间有一条道路。

思路:实际上是贪心的想法。首先考虑原图(不删除任何边),其最小代价是每个连通分支中的最小代价之和。下面考虑删边,如果可以继续删边而不增加连通分支数量,那么最小代价不会增加。直到将原图删成了一棵森林。如果还需要继续删边,那么必然会增加代价,而显然增加的量为没有计入总代价的代价最小点的代价(设为t点)。因为总可以在t所在的连通分支中将t与之前此连通分支内的最小代价点分开。如此删除直到删除符合题意的边为止。

一开始大数据没有过,因为我在读入数据后先排序,然后每找到一个连通分支的最小代价就标记相应位置的数已经选取。实际上这样的话如果原始的图没有边,那么复杂度变成了O(n^2),所以会TLE。实际上在深搜判连通的时候再产生t数组,最后排序扫一遍即可,复杂度O(nlogn)。(转载)

http://hihocoder.com/problemset/problem/1160

#include<iostream>  #include<algorithm>  #include<string>  #include<map>  #include<vector>  #include<cmath>  #include<string.h>  #include<stdlib.h>  #include<cstdio>  #define ll long long  using namespace std;vector<int> g[1000001];int vis[1000001];int x[1000001];int f[1000001];int cnt,w,c,u;void dfs(int p){if(vis[p]==1)return;vis[p]=1;if(w>x[p]){if(w!=100000000) //为了不把连通分支中最小的一个值导入 f[++c]=w;w=x[p];}elsef[++c]=x[p];for(int i=0;i<g[p].size();++i)dfs(g[p][i]);}int main(){int t;scanf("%d",&t);  u=0;while(t--){int n,m,k;scanf("%d %d %d",&n,&m,&k);  for(int i=0;i<n;++i){scanf("%d",&x[i]);  g[i].clear();}int a,b;for(int i=0;i<m;++i){scanf("%d %d",&a,&b);  a--;b--;g[a].push_back(b);g[b].push_back(a);}memset(vis,0,sizeof(vis));cnt=0;w=0;ll s=0;c=-1;for(int i=0;i<n;++i){if(vis[i]==0){w=100000000;dfs(i);s+=w;     //w求得为每个连通分支的最小代价  cnt++;    //连通分支数  }}sort(f,f+c+1);int ss=k-(m-(n-cnt));  //n-cnt:原图为森林时的边数  m-(n-cnt):将原图变成森林需要删的边数 for(int i=0,j=ss;i<=c,j>0;++i,j--){s+=f[i];} printf("Case #%d: %lld\n",++u,s);  }return 0;}


0 0
原创粉丝点击