(intermediate)DFS (强连通+缩点+拓扑排序) UVA 11098 - Battle II

Problem E
Battle II
Time Limit: 6 seconds

 

"Marriage, in life, is like a duel in the
midst of the battle
"

Edmond About

Daltons (Daida, Alhar, Tara, and Reyton) love playing games. One of their favorite games is 'Battle II'. In order to play this game, first, one of them is chosen as a problem-setter. The problem-setter starts drawing some bombs on a piece of paper (each bomb is a circle: has a center and a radius). She then associates to each bomb a destruction range. There are three rules defined in this game:

  1. If a bomb explodes, all the bombs in its destruction range will also explode. A bomb b1 is in destruction range of  another bomb b2 if distance of b2 from the perimeter of b1 is less than the range of  b1. (i.e. b2.range + b1.radius + b2.radius >= Distance(b1.center, b2.center))
  2. A bomb may explode due to either being affected by explosion of another bomb according to the first rule or manually being fired.
  3. Firing a bomb manually has a cost which is equal to the range of it.

She finally gives the configuration of the bombs to the others and asks them to find a sequence of bombs to fire which should satisfy the following conditions:

  1. All the bombs should be exploded as a result of firing and explosion of the bombs in this sequence. 
  2. The ith bomb in the sequence should not result explosion of the jth bomb where j > i
  3. The average cost of firing the bombs that are in the sequence must be minimum.

  You should help the players find the solution to this problem by writing a program which is able to find such a sequence given the specifications and configuration of the bombs in the paper.

Input

The first line of input gives the number of cases, N. N test cases will follow. Each one starts with a line containing the number of bombs ( 0< n<=300 ). Each of  the next n lines contains four integers Xi, Yi, Ri, Ei, meaning that the i-th bomb is located at (Xi, Yi), has a radius of Ri, and has a range of Ei. There will be a blank line after each block of test case.

Output

For each test case, output the line containing "Case #x:", followed by list of bombs in the order that should be fired, seperated by a single space. Follow the output format used in sample output. If there are more than one solution, any of them is acceptable.

Sample Input                               Output for Sample Input

1
3
4 7 2 2
8 5 1 0
3 -3 1 1
 
Case #1: 1 0 2

题意:给出一些炸弹的位置和爆炸范围,问怎么引爆使得平均消费最小。

思路:首先根据题目的不等式,我们能构成一个有向图,然后根据这个有向图,我们先找出各个强连通分量,每个强连通分量里面只要引爆其中一个炸弹,这个分量里面的炸弹都会随之引爆。然后我们怎么求引爆哪些才能使平均消费最小呢? 首先平均值是这样的S/n,当我们再加一个数的时候有(S+x)/(n+1) 只要S/n > (S+x)/(n+1) 那么就能使得平均值变小。那么对于一个强连通分量里面,我们只能引爆一个炸弹,那么我们肯定是引爆那个费用最小的。然后又几个入度为0的强连通分量需要手动引爆,我们先把他们排除出去,然后拓扑排序一下顺序,然后剩下的分量拿出来按费用排序,如果费用比当前平均值小就手动引爆,最后按照拓扑排序的顺序,反过来输出就行了。

。。。怎么图论的题目都要写这么长啊。。。

代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<math.h>
using namespace std;
#define eps 1e-8
#define LL long long
const int maxn = 300+5;

struct Bomb
{
	int x , y , r , e;
	int index;
}bomb[maxn];

inline bool operator < (const Bomb & b1 , const Bomb & b2)
{
	if (b1.e==b2.e) return b1.index < b2.index;
	return b1.e < b2.e;
}

struct Ans
{
	Ans(int dh,int ct,int ix,int no) : depth(dh) , cost(ct) , index(ix) , sccno(no) { }
	int depth;
	int cost;
	int index;
	int sccno;
};

inline bool cmp_cost(const Ans & a1,const Ans & a2)
{
	if (a1.cost==a2.cost) return a1.index < a2.index;
	return a1.cost < a2.cost;
}

inline bool cmp_depth(const Ans & a1,const Ans & a2)
{
	if (a1.depth==a2.depth) return a1.index > a2.index;
	return a1.depth > a2.depth;
}

int n , dfn , scc_cnt , sccno[maxn] , pre[maxn] , low[maxn];
int ind[maxn];  //入度(用于拓扑排序)
bool vis[maxn];
vector<int> G1[maxn];   //原图
vector<int> G2[maxn];   //缩点图
stack<int> S;
vector<Bomb> scc[maxn];  //强连通分量的点

inline LL sqr(LL x) { return x*x; }
inline LL dist(int i,int j) { return sqr(bomb[i].x-bomb[j].x)+sqr(bomb[i].y-bomb[j].y); }

inline bool connect(int i,int j)
{
	return dist(i,j)  <= sqr(bomb[i].r+bomb[j].r+bomb[i].e);
}

void input()
{
	for (int i = 0 ; i < n ; ++i) G1[i].clear();
	for (int i = 0 ; i < n ; ++i)
	{
		scanf("%d%d%d%d",&bomb[i].x,&bomb[i].y,&bomb[i].r,&bomb[i].e);
		bomb[i].index = i;
	}
	for (int i = 0 ; i < n ; ++i) {
		for (int j = 0 ; j < n ; ++j) if (i!=j && connect(i,j)) 
		{
			G1[i].push_back(j);
		}
	}
}

void dfs(int u)
{
	pre[u] = low[u] = ++dfn;
	S.push(u);
	for (int i = 0 ; i < G1[u].size() ; ++i)
	{
		int v = G1[u][i];
		if (!pre[v])
		{
			dfs(v);
			low[u] = min(low[u],low[v]);
		} else if (!sccno[v])
			low[u] = min(low[u],pre[v]);
	}
	if (pre[u]==low[u]) {
		++scc_cnt;
		scc[scc_cnt].clear();
		G2[scc_cnt].clear();
		while (true)
		{
			int x = S.top(); S.pop();
			sccno[x] = scc_cnt;
			scc[scc_cnt].push_back(bomb[x]);
			if (x==u) break;
		}
	}
}

//找强连通分量并缩点构图
void find_scc()
{
	dfn = scc_cnt = 0;
	memset(pre,0,sizeof(pre));
	memset(low,0,sizeof(low));
	memset(sccno,0,sizeof(sccno));
	for (int i = 0 ; i < n ; ++i) if (!pre[i])
		dfs(i);
	memset(ind,0,sizeof(ind));
	for (int i = 1 ; i <= scc_cnt ; ++i)
	{
		memset(vis,0,sizeof(vis));
		vis[i] = true;
		for (int j = 0 ; j < scc[i].size() ; ++j) {
			int u = scc[i][j].index;
			for (int k = 0 ; k < G1[u].size() ; ++k) {
				int v = G1[u][k];
				if (vis[sccno[v]]) continue;
				vis[sccno[v]] = true;
				G2[sccno[u]].push_back(sccno[v]);
				++ind[sccno[v]];
			}
		}
	}
}



void solve()
{
	find_scc();
	memset(vis,0,sizeof(vis));
	vector<Ans> ans1;
	for (int i = 1 ; i <= scc_cnt ; ++i) sort(scc[i].begin(),scc[i].end());
	stack<int> out;
	int sum = 0;
	int cnt = 0;
	queue<Ans> q;
	for (int i = 1 ; i <= scc_cnt ; ++i) 
	{
		if (ind[i]==0) { 
			sum += scc[i][0].e;
			++cnt;
			q.push(Ans(0,scc[i][0].e,scc[i][0].index,i));
			out.push(scc[i][0].index); 
		}
	}
	while (q.size())
	{
		Ans tmp = q.front(); q.pop();
		if (tmp.depth) ans1.push_back(tmp);
		int u = tmp.sccno;
		for (int i = 0 ; i < G2[u].size() ; ++i)
		{
			int v = G2[u][i];
			--ind[v];
			if (ind[v]==0) q.push(Ans(tmp.depth+1,scc[v][0].e,scc[v][0].index,v));
		}
	}
	sort(ans1.begin(),ans1.end(),cmp_cost);
	vector<Ans> ans2;
	for (int i = 0 ; i < ans1.size() ; ++i)
	{
		if (sum > cnt * ans1[i].cost)
		{
			sum += ans1[i].cost;
			++cnt;
			ans2.push_back(ans1[i]);
		}
	}
	sort(ans2.begin(),ans2.end(),cmp_depth);
	for (int i = ans2.size()-1 ; i >= 0 ; --i) out.push(ans2[i].index);
	while (out.size())
	{
		printf(" %d",out.top());
		out.pop();
	}
	cout << endl;
}

int main()
{
	int T; cin>>T;
	int k = 0;
	while (T--)
	{
		++k;
		scanf("%d",&n);
		input();
		printf("Case #%d:",k);
		solve();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值