題意:
在一條直線上有C個城市,分別屬於N個國家,需要炸燬至少K個國家的城市,花費的金額爲兩端城市的距離,國家間有M個特殊關係,有關係的兩個國家中最多隻能摧毀一個國家的城市,關係沒有傳遞性,求所需要的最小花費,如果無法完成輸出-1。只能炸一次且不一定要炸掉線段上所有的城市。
解題思路:最先考慮的就是用什麼模型了。
假如有a個國家,有的國家之間有邊相連,問選哪些國家使得選中的國家之間沒有邊且國家數最多。
轉化一下就是 最大獨立集。
最大獨立集能求的貌似只有二分圖和樹了,
題目中說了沒有環存在,所以就用樹上的最大獨立集來求了。(近似於貪心,不會的網上找吧)
接下來就是枚舉了,
枚舉一個區間,區間裏 最大獨立集 就是在這個區間最多能炸掉的國家數。
如果最多能炸掉的國家數>=K的話,就說明這個區間滿足要求了。
不過這裏枚舉也有技巧,如果是暴利枚舉的話,O(N^2)的複雜度肯定TLE,
好在之前有過類似思路,能轉化爲O(N)的複雜度。(這種思路建議保留一下,很多優化中能用)
我的city是從0到c-1存的, 最開始給個邊界值 l=0,r=0;
如果在(l,r)的區間滿足條件,記錄答案,並把l右移一位,
否則把r右移一位,
直到r==c結束。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
#define FOR(i,l,r) for(int i=(l);i<=(r);++i)
#define REP(i,n) for(int i=0;i<(n);++i)
#define DSC(i,r,l) for(int i=(r);i>=(l);--i)
#define N 2110
#define M 2110
#define INF 2000000000
struct
{
int to,next;
}edge[M];
int head[N],ip;
bool visit[N],flag[N];
void add(int u,int v)
{
edge[ip].to=v;edge[ip].next=head[u];head[u]=ip++;
edge[ip].to=u;edge[ip].next=head[v];head[v]=ip++;
}
struct Node
{
int x;
int fa;
bool operator<(const Node a)const
{
return x<a.x;
}
}node[5010];
struct
{
int x,y;
}f[1010];
void dfs(int pos,int pre,int &num)//貪心法求樹上的最大獨立集
{
visit[pos]=0;
int to;
for(int p=head[pos];p!=-1;p=edge[p].next)
{
to=edge[p].to;
if(!visit[to]) continue;
dfs(to,pos,num);
}
if(!flag[pos])
{
flag[pos]=flag[pre]=1;
num++;
}
}
bool solve(int l,int r,int k,int m)
{
memset(visit,0,sizeof(visit));
memset(head,-1,sizeof(head)); ip=0;
memset(flag,0,sizeof(flag));
int num=0;
FOR(i,l,r) if(!visit[node[i].fa]) visit[node[i].fa]=1; //記錄區間內存在的國家
REP(i,m)
{
if(visit[f[i].x] && visit[f[i].y] )
{
add(f[i].x,f[i].y);//在存在的國家間建邊
}
}
FOR(i,l,r)
{
if(visit[ node[i].fa ]) dfs(node[i].fa,node[i].fa,num); //求每棵樹上得最大獨立集
}
if(num>=k) return 1;
return 0;
}
int aaaa(int c,int n,int k,int m)
{
if(!solve(0,c-1,k,m)) return -1;
if(k<=1) return 0;//如果K<=1 ,隨意炸掉一個城市就夠了
int l=0,r=0;
int ans=2e9;
while(r<c)
{
if(solve(l,r,k,m))
{
ans=min(ans,node[r].x-node[l].x);
l++;
}
else r++;
}
return ans;
}
int main()
{
int cas,cas1=1;
cin>>cas;
int c,n,k,m;
while(cas--)
{
cin>>c>>n>>k>>m;
REP(i,c) scanf("%d%d",&node[i].x,&node[i].fa);
REP(i,m) scanf("%d%d",&f[i].x,&f[i].y);
sort(node,node+c);//對原先的city按座標排一次序
printf("Case #%d: %d\n",cas1++,aaaa(c,n,k,m));
}
return 0;
}