http://poj.org/problem?id=1201
给定一个数轴上的 n n n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ a i , b i ] [a_i, b_i] [ai,bi] 里至少有 c i c_i ci 个点。
输入第一行一个整数 n n n 表示区间的个数,接下来的 n n n 行,第i行有三个数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,用空格隔开。 1 < = n < = 50000 , 0 < = a i < = b i < = 50000 1 <= n <= 50000, 0 <= ai <= bi <= 50000 1<=n<=50000,0<=ai<=bi<=50000 并且 1 < = c i < = b i − a i + 1 1 <= ci <= bi - ai+1 1<=ci<=bi−ai+1。
输出一个整数表示最少选取的点的个数。
本题的题意可以抽象为一系列的不等式约束,因此可以考虑使用差分约束系统的方法来解决。
假设 s u m [ i ] sum[i] sum[i]是区间 [ 0 , i − 1 ] [0, i-1] [0,i−1]的最少选点个数,那么题意可以描述为 s u m [ b i + 1 ] − s u m [ a i ] ≥ c i ( 1 ≤ i ≤ n ) sum[b_i+1]-sum[a_i]\geq c_i \ (1\leq i \leq n ) sum[bi+1]−sum[ai]≥ci (1≤i≤n)。
但是,除此之外,还要注意一些隐含的条件: 0 ≤ s u m [ i + 1 ] − s u m [ i ] ≤ 1 0\leq sum[i+1]-sum[i] \leq 1 0≤sum[i+1]−sum[i]≤1。 s u m [ i + 1 ] − s u m [ i ] ≤ 1 sum[i+1]-sum[i] \leq 1 sum[i+1]−sum[i]≤1可以转换为 s u m [ i ] − s u m [ i + 1 ] ≥ − 1 sum[i]-sum[i+1]\geq-1 sum[i]−sum[i+1]≥−1。
于是,将该问题转化为求最长路的问题,通过spfa算法即可求得答案: s u m [ max ( b i ) + 1 ] ( 1 ≤ i ≤ n ) sum[\max(b_i)+1](1\leq i \leq n) sum[max(bi)+1](1≤i≤n)
#include
#include
#include
using namespace std;
#define inf 500000000
struct Edge{
int v, w, nxt;
}e[500005];
int head[50005];
int cnt=1;
int dis[50005], inq[50005];
queue<int> Q;
void add(int u, int v, int w)
{
e[cnt].v = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt;
cnt++;
}
void spfa()
{
inq[0]=1;
dis[0]=0;
Q.push(0);
while(!Q.empty())
{
int u = Q.front();Q.pop();inq[u]=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v = e[i].v;
int w = e[i].w;
if(dis[v] < dis[u] + w)
{
dis[v] = dis[u] + w;
if(!inq[v])
{
inq[v]=1;
Q.push(v);
}
}
}
}
}
int n, a, b, c;
int maxy;
int main ()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a>>b>>c;
maxy = max(b, maxy);
add(a, b+1, c);
}
for(int i=0;i<=maxy;i++)
{
dis[i] = -inf;
inq[i] = 0;
}
for(int i=0;i<=maxy;i++)
{
add(i, i+1, 0);
add(i+1, i, -1);
}
spfa();
cout<<dis[maxy+1]<<endl;
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=1285
N只小猫咪,从1到N编号,它们进行了M场比赛,每场比赛的胜负已知,现在要按照比赛结果对小猫咪进行排序(比赛结果具有传递性,获胜的猫咪排在当场比赛失败的猫咪前面),要求输出最小字典序的序列。
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。
给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
本题是一个典型的拓扑排序问题,把每只猫咪看成一个点,猫咪A赢了B看做A到B有一条有向边,从而可以建立起有向图。
维护一个数组indeg记录每个点的入度,以及一个拓扑序列数组(起初为空。本题也可直接输出,不使用拓扑序列数组),入度为0的点可取出并添加到拓扑序列的最后,并在图中删除该点及其关联的有向边。
为了使得字典序最小,可以使用一个最小堆,将所有入度为0的点加入最小堆中,每次从堆顶取出点,添加到拓扑序列的最后,并在图中删除。
#include
#include
#include
using namespace std;
vector<int> G[505];
int N, M, A, B;
int indeg[505];
priority_queue<int> pq;
int main ()
{
while(cin>>N>>M)
{
for(int i=0;i<=N;i++)
{
G[i].clear();
indeg[i]=0;
}
for(int i=0;i<M;i++)
{
cin>>A>>B;
G[A].push_back(B);
indeg[B]++;
}
for(int i=1;i<=N;i++)
{
if(!indeg[i]) pq.push(-i);
}
bool first = true;
while(!pq.empty())
{
if(!first) cout<<" ";
else first = false;
int u = -pq.top(); pq.pop();
for(int i=0, s=G[u].size();i<s;i++)
{
int v = G[u][i];
indeg[v]--;
if(!indeg[v]) pq.push(-v);
}
cout<<u;
}
cout<<endl;
}
return 0;
}
https://vjudge.net/problem/HDU-3639
大学班级选班长,N 个同学(编号从0到N-1)均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适。求最高票数和获得最高票数的同学编号。
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格! 这个题可以看做一个图论问题,每个同学当做一个点,如果同学A认为同学B合适,则认为点A和点B之间有一条有向边。 由于投票具有传递性,一个强连通分支中的点一定会获得相同的票数。而且如果存在从强连通分支S1到达强连通分支S2的有向边,那么S2中的点获得票数一定高于S1。 我们可以将每个强连通分支看成一个点,若存在从S1到S2的有向边,则看做两点之间存在一条有向边,从而实现缩点,得到一个缩点后的图sG。这个图中,只有出度为0的点对应的强连通分量才可能获得最大票数,而这类点的票数 = 所有可以到达该点的强连通分量包含的人数之和 + 该点对应强连通分量包含人数 - 1。 具体可分为以下过程:输出
基本思路
解题时遇到的坑
完整代码
#include