HDU 4354 Missile 树的最大独立集+枚举

题意:

在一条直线上有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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值