带权的区间调度问题,贪心似乎不好解决,一般的方法是动态规划
题目:点我
细节分析: 对于一个乌龟说的话,前面有a个乌龟,后面有b个乌龟,意为 该乌龟可能是第[a+1,n-b]名。
题目解法:
一个区间表示了一个名次,区间的长度表示这个名次有多少人。
给区间附上权值,如果有x个人的名次为区间[a,b],那么区间[a,b]的权值val为min(b-a+1,x)。
代表区间[a,b]最多可能有val个人说真话。
先求最多有多少人说真话。
说真话的最多人数 等价于在若干种区间内选取互不重叠的区间集合,使的权值和最大。
细节理解:
假如一个区间的长度为5,而实际上只出现了1个这样的区间,可以理解为1个人说了真话,该区间的其他人都说了假话。
假如一个区间的长度为5,而实际上出现了9个这样的区间,可以理解为5个人说了真话,其余人说了假话。
假如两个区间重叠,比如[1,4]和[4,7],那么不能同时选取,因为[1,4]意为前面4个人同名次,后面是其他名次,而[4,7]意为第4人到第7人同名次,前面是其它名次,矛盾。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#define __len(a,b) (b-a+1);
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=1000 ;
int n;
struct Seg
{
int le,ri,val;
Seg(){}
Seg(int le,int ri ):le(le),ri(ri){val=1;}
} a[maxn+5];
vector<int >G[maxn+5];
int dp[maxn+5];
int best[maxn+5];
bool vis[maxn+5];
void work()
{
dp[0]=0;
best[0]=-1;
for(int i=1;i<=n;i++)
{
dp[i]=dp[i-1];
best[i]=-1;
for(int j=0;j<G[i].size();j++)
{
int id=G[i][j];
int p=a[id].le-1;
if(dp[p]+a[id].val>dp[i])
{
dp[i]=dp[p]+a[id].val;
best[i]=id;
}
}
}
printf("%d",n-dp[n]);
memset(vis,0,sizeof vis);
int s=n;
while(s)
{
while(s&&best[s]==-1) s--;
if(!s) break;
int id=best[s];
int le=a[id].le;
int cnt=a[id].val;
for(int i=1;cnt&&i<=n;i++)
{
if(a[i].le==le&&a[i].ri==s)
{
vis[i]=1;
cnt--;
}
}
s=le-1;
}
for(int i=1;i<=n;i++)
{
if(!vis[i]) printf(" %d",i);
}
putchar('\n');
}
void init()
{
for(int i=1;i<=n;i++)
{
G[i].clear();
}
}
int main()
{
int x,y;
while(~scanf("%d",&n))
{
init();
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
int le=x+1;
int ri=n-y;
a[i]=Seg(le,ri);
if(le>ri) continue;
bool ok=0;
for(int j=0;!ok &&j<G[ri].size();j++)
{
int id=G[ri][j];
Seg &now=a[id];
if(now.le==le&&now.ri==ri)
{
int val=now.val;
now.val=min(ri-le+1,val+1 );//我的老天,先写成了le-ri+1
ok=1;
break;
}
}
if(ok) continue;
G[ri].push_back(i);
}
work();
}
return 0;
}