wannafly 挑战赛 C (选k种线段不相交最长 - 状压dp)

大致题意

维坐标轴上n条线段,每条线段左端点l,右端点r,颜色为c,从中选m种颜色的互不接触的线段,每种颜色可选多条,所选线段的总长度最长为多少?

1 <= n <= 100000; 1 <= m <= 7;
1 <= l < r <= 1000000000; 1 <= c <= 7;

输入

第一行2个整数 n, m;
接下来n行,每行3个整数l, r, c。

输出

一个整数,表示所选线段的最长的总长度;若选不了,输出-1;

思路

颜色只有7种,容易想到状压。先将线段按右端点升序排序。算一下内存 1 k = 256 int,完全可以存的下状态 dp[i][j]: 表示前 i 条线段 有 颜色状态 j 时候的最长距离。
转移对于我这种蒟蒻来说有点难想:可以这样理解:

  1. 如果不选当前线段:那么将所有状态继承 i-1
  2. 如果选择当前线段:要二分查找到最后一个右端点小于等于当前线段左端点的线段 k ,进行转移。由状态转移1知道 k 前面的状态都不用管,因为 k 继承了前面的最优状态。
  3. 前两者取大

代码:

发现原来lower_bound可以二分查结构体,不过要先重载运算符,而且要写node{} 不能简单写 {}。

#include
using namespace std;
#define maxn 100005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (1ll*x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 10007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define mkp make_pair
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n,m,dp[maxn][1<<8];
struct seg{int l,r,c;
    bool operator < (const seg &rid)const{
        return r < rid.r;
    }
}a[maxn];
int calc(int x){
    int res=0;
    while(x){
        res+=x&1;
        x>>=1;
    }
    return res;
}
int main()
{
    n=read();m=read();int up=(1<<7)-1;
    inc(i,1,n)a[i].l=read(),a[i].r=read(),a[i].c=(1<<(read()-1));
    sort(a+1,a+n+1);
    inc(i,1,n)dp[i][a[i].c]=a[i].r-a[i].l;
    inc(i,1,n){
        int k=lower_bound(a+1,a+n+1,seg{0,a[i].l,0})-a-1;///lowerbound也可以找结构体居然 只不过要写全node{}
        inc(j,0,up){
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
            if(dp[k][j])dp[i][j|a[i].c]=max(dp[i][j|a[i].c],dp[k][j]+a[i].r-a[i].l);
        }
    }
    int ans=-1;
    inc(i,0,up)if(calc(i)==m)ans=max(ans,dp[n][i]);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(动态规划-状压)