题目的大意是给定若干(a,b,c)表示[a,b]之间有c个1,序列只能是0或者1,最后问在这些约束下,序列的最小长度。稍微变形就成了典型的差分约束问题,转化成S(b)-S(a)<=c这样的问题,然后用算导上的建模过程,a->b连条边,权值为c,因为这样的话,对于最后的dis(a)和dis(b),需要符合dis(b)<=dis(a)+w(a->b),也就是dis(b)-dis(a)<=w(a->b)。所以在加个虚拟的约束点后,点集v={v0,v1...vn}的解就是dis(0),dis(1)...dis(n){dis(i)表示建图后v0到vi的最短路},根据上述就能推出所对应的约束条件了。而这题就是求S(max)-S(min)的最小值,就是求Vmax到Vmin的最短距离,需要注意的是还有这样的约束条件0<=S(i+1)-S(i)<=1,这样同时也保证了图的连通性。对于POJ1364 King这题,是类似的,不过题目只给出"<"或者">",要转换,同时为保证连通性,可以这样处理,先将这n个点push到队列中,但这只算了虚节点的入队次数,但不管这些,入队n次以上,肯定是有负环的,可以反证。不过我后来加了个-1000<=S(i+1)-S(i)<=1000,这样一个条件,也AC,想法就是保证了它的连通性。
感谢:
http://www.cppblog.com/initiate/archive/2010/04/03/111530.html
http://blog.csdn.net/ChinaCzy/archive/2010/07/26/5767025.aspx
#include < iostream >
#include < vector >
#include < queue >
using namespace std;
const int MAX = 50005 ;
struct NODE
{
int v, w;
NODE() {}
NODE( int vv, int ww) : v(vv), w(ww) {}
};
vector < vector < NODE > > mm;
int in [MAX];
int dis[MAX];
int spfa( int beg, int end)
{
memset( in , 0 , sizeof ( in ));
memset(dis, - 1 , sizeof (dis));
queue < int > Q;
in [beg] = 1 ;
dis[beg] = 0 ;
Q.push(beg);
while ( ! Q.empty())
{
int u = Q.front();
Q.pop();
in [u] = 0 ;
for ( int i = 0 ; i < mm[u].size(); i ++ )
{
int v = mm[u][i].v;
int w = mm[u][i].w;
if (dis[u] + w > dis[v])
{
dis[v] = dis[u] + w;
if ( in [v] == 0 ) Q.push(v), in [v] = 1 ;
}
}
}
return dis[end];
}
int main()
{
int cnt;
mm.clear();
mm.resize(MAX);
scanf( " %d " , & cnt);
int minv = MAX, maxv = - 1 ;
while (cnt -- )
{
int a, b, c;
scanf( " %d%d%d " , & a, & b, & c);
b ++ ;
minv = min(minv, a);
maxv = max(maxv, b);
// s(b) - s(a) >= c
mm[b].push_back(NODE(a, c));
}
// 0 <= s(i + 1) - s(i) <= 1
for ( int i = minv; i <= maxv - 1 ; i ++ )
{
mm[i + 1 ].push_back(NODE(i, 0 ));
mm[i].push_back(NODE(i + 1 , - 1 ));
}
// 必有解,无需判负环
printf( " %d\n " , spfa(maxv, minv));
}