P1986 元旦晚会——贪心或差分约束系统
每个人可能属于不同的声部,每个声部最少要有c[i]个人发声;
求最少需要多少话筒;
首先贪心,将所有声部的区间按照右端点大小排序,如果右端点相同,左端点从小到大排序;
贪心每次选取靠近右端点的,这样每个区间相交的是最多的。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=130010; struct node { int l,r,c; }a[maxn]; bool cmp(node qw,node we) { if(qw.r!=we.r) return qw.r<we.r; return qw.l<we.l; } int n,m; int ans,vis[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].c); } sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++) { int k=0; for(int j=a[i].r;j>=a[i].l;j--) { if(vis[j]) k++; } if(k>=a[i].c) continue; else { for(int h=a[i].r;(h>=a[i].r-a[i].c+1)&&(k<a[i].c);h--) { if(!vis[h]) { vis[h]=1; ans++; k++; } } } } printf("%d",ans); return 0; }
差分约束系统:
将左右端点连边,边权是c[i];因为要保持图的联通,实际连边是x-1和y;
将i和i-1连一条-1的边,将i和i+1连一条0边;
跑一边最长路即为所求答案。
我们所求的实际上是不等式dis[y]-dis[x]>=c[i];
求最长路时的松弛操作正是将这个不等式一点点的满足;
为什么要将i和i-1连-1边呢?
因为可能是这种情况:
-1其实是将重合的部分减去
#include<cstring> #include<cstdio> #include<queue> #include<algorithm> using namespace std; const int maxn=1e6+10; int pre[maxn*2],last[maxn],other[maxn*2],len[maxn*2],l; void add(int x,int y,int z) { l++; pre[l]=last[x]; last[x]=l; other[l]=y; len[l]=z; } queue<int> q; int n,m; int dis[maxn]; bool vis[maxn]; void spfa() { while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int p=last[x];p;p=pre[p]) { int v=other[p]; if(dis[v]<dis[x]+len[p]) { dis[v]=dis[x]+len[p]; if(!vis[v]) vis[v]=1,q.push(v); } } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x-1,y,z); } for(int i=1;i<=n;i++) { add(i,i-1,-1); add(i-1,i,0); } for(int i=1;i<=n;i++) q.push(i),vis[i]=1; spfa(); printf("%d",dis[n]); return 0; }