timus 1303. Minimal Coveragen URAL 解題報告 貪心或者DP

 timus  1303. Minimal Coveragen    URAL  解題報告    貪心或者DP

用最少的線段去覆蓋一個大區間,由於邊數最多 The set contains at least one and at most 99999 segments!      大區間最大0--5000,如果一般的DP的話可能超時,dp[i]表示如果用邊i的話覆蓋到當前位置所用的最少線段數目,如果成功覆蓋的話記錄最後所用的線段的編號,用k記錄!  也是N*N的算法,但是時間竟然沒卡主……  神了,  當然這個題我沒用這個方法去做,因爲這個過了也是偶然,不明白爲什麼會被劃分到DP題組裏面,也許有更好的做法,但是到目前爲止還沒發現……       網上搜不到DP解決的好辦法!

正規解法應該是劉汝佳書上的貪心做法!
貪心的話,就是先按照x升序排列,然後能覆蓋區間左端的前提下,儘可能使得y較大,然後從這個y爲區間的左端點繼續覆蓋知道覆蓋完……
這個題貌似可能有多種答案,應該用到特判…… 
比較糾結的是網上看到一個貪心的解法,博主說提交總是WA,我查了好久也沒查出來,UVA上也過不了,求幫助http://www.cnblogs.com/gj-Acit/p/3207666.html


現在先貼上DP的效率比較低的算法,雖然經過一點小優化,但是個人感覺不樂觀,雖然過掉了這個題!    兩外路徑回溯的時候是記錄該點他爹,往回回溯就好……代碼來源,這個我就不想敲了:http://www.cnblogs.com/liulangye/archive/2012/08/24/2653775.html
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long

using namespace std;
const int N=100005;
const int INF=0x6fffffff;
struct node
{
    int x,y;
}mem[N];
int f[N];
int sum[N];
int ans[N];
bool cmp(node a,node b)
{
    if(a.x==b.x) return a.y>b.y;
    return a.x<b.x;
}
void Fans(int l,int n)
{///???
    for(int i=n;i>=1;--i)
    {
        ans[i]=l;
        l=f[l];
    }
}
int main()
{
    //freopen("data","r",stdin);
    int m;
    int x,y;
    scanf("%d",&m);
    int I=0;
    while(scanf("%d %d",&x,&y))
    {
        if(x==0&&y==0)
            break;
        if((x>=m&&y<=0)||(x==y))
            continue;
        mem[I].x=x;mem[I].y=y;++I;
    }
    sort(mem,mem+I,cmp);
    memset(sum,-1,sizeof(sum));
    int k=-1;
    if(mem[0].x>0)
    {
         printf("No solution\n");   return 0;
    }
    int n=I;
    I=0;
    for(int i=0;i<n;++i)
    {///把一些不必要的邊去掉
        int l=0;
        if(i>0&&mem[i].x!=mem[I-1].x)
        for(l=0;l<I;++l)
        {
            if(mem[l].x<=mem[i].x&&mem[i].y<=mem[l].y)
            break;
        }
        if(l==I)
        {mem[I].x=mem[i].x;mem[I].y=mem[i].y;++I;}
    }


    for(int i=0;i<I;++i)
    {
        int x=mem[i].x;
        int y=mem[i].y;
        if(i>0&&x==mem[i-1].x)
        continue;///根據已經排好的順序,x相同話y肯定是前面的大
        if(x<=0)
        {///???
            sum[i]=1;
            f[i]=-1;///第一條邊的父親當然是-1了
            if(y>=m&&(k==-1||sum[i]<sum[k]))
            {
                k=i;///k記錄最少的sum的下標,最後根據這個來
            }
        }else
        {
            int temp=INF;int l=-1;
            for(int j=0;j<i;++j)
            {///仍然是一個O(n*n)的算法,竟然沒超時,數據是不是有點弱啊   邊數可是最多有10W條啊
                if(sum[j]!=-1&&mem[j].y>=x&&sum[j]<temp)
                {///sum[i]==-1表示邊i沒用
                    temp=sum[j];l=j;
                }
            }
            if(l!=-1)
            {
                sum[i]=sum[l]+1;///急用
                f[i]=l;///記錄父親
                if(y>=m&&(k==-1||sum[i]<sum[k]))
                {
                    k=i;///記錄用的最少的邊的號,前提是y>m也就說已經覆蓋完了,完了之後才能更新答案
                }
            }
        }
    }


    if(k==-1)
    printf("No solution\n");
    else
    {
        printf("%d\n",sum[k]);
        Fans(k,sum[k]);///尋找父親,路徑回溯啊!
        for(int i=1;i<=sum[k];++i)
        {
            printf("%d %d\n",mem[ans[i]].x,mem[ans[i]].y);
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章