总体感觉,难点是建图,因为建图的时候需要考虑一些题目上没有明确给出的隐含条件,只有把所有约束关系找全之后,然后再正确运用最短路(或者最长路)的性质求解,才能得到正确答案。
说说我的收获:
node1:对于区间放置元素问题,要注意区间开闭性,也就是说要关注对点的约束。特别注意每个点上放置元素个数的限制,这里一般都是隐含关系的考察点(详见下文)。
node2:对于差分不等式,a - b <= c ,建一条 b 到 a 的权值为 c 的边,求的是最短路,得到的是最大值;对于不等式 a - b >= c ,建一条 b 到 a 的权值为 c 的边,求的是最长路,得到的是最小值。存在负环的话是无解,求不出最短路(dist[ ]没有得到更新)的话是任意解。
node3:建图中有时候会用到一个虚点,这个点到图中每个实点的距离(dist[ ])为0,当然这个点的作用是方便图中的点入队(spfa算法),然后使这些实点的dist[ ]值得到更新,其实有时候我们可以省略这个点,手动把所有实点入队,同时更新这些实点的 dist[ ] 值和 visit[ ] 值。
POJ1201/ZOJ1508/HDU1384 Intervals(解题报告)
这道题就是整数区间问题,典型的差分约束问题,题目要求了每个点要么不放元素,要么放一个元素,那么我们就可以根据这个要求加边:0 <= s[ i+1 ] - s[ i ] <= 1 。这就是上面说的隐含条件的应用。
POJ1716 Integer Intervals
这个题是上面那个的阉割版,把每个区间端点的大小关系设为了定值,其他的一样。
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<queue>
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;
const int N = 10010;
struct Edge{
int s,e,v,next;
}edge[3*N];
int m,e_num,left,right,head[N],vis[N],dist[N];
void AddEdge(int a,int b,int c){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
edge[e_num].next=head[a]; head[a]=e_num++;
}
queue <int> q;
void getmap(){
int i,a,b;
e_num=0; left=INT_MAX; right=INT_MIN;
memset(head,-1,sizeof(head));
while(m--){
scanf("%d%d",&a,&b);
AddEdge(a,b+1,2);
left=min(left,a);
right=max(right,b);
}
for(i=left;i<=right;i++){
AddEdge(i,i+1,0);
AddEdge(i+1,i,-1);
}
for(i=left;i<=right;i++){
q.push(i);
vis[i]=1;
dist[i]=0;
}
}
void spfa(){
int i;
while(!q.empty()){
int cur=q.front();
q.pop();
vis[cur]=0;
for(i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(dist[u]-dist[cur]<edge[i].v){
dist[u]=dist[cur]+edge[i].v;
if(!vis[u]){
q.push(u); vis[u]=1;
}
}
}
}
printf("%d\n",dist[right+1]);
}
int main()
{
while(~scanf("%d",&m))
{
getmap();
spfa();
}
return 0;
}
详见结题报告
POJ3159 Candies
这个题不卡建图,所有约束关系都给你了,只要根据给的不等式建边就好,不过,用spfa的话,需要注意,队列会超时的……
貌似是出数据的故意这么玩的,改成栈吧,由于栈和队的性质差异(一个后进先出,一个先进先出),所以对于卡队列的数据用栈可以秒杀,当然反过来也一样,我这个题开始用的是队列spfa,超时了,听大神 XH 指点,把队改成栈,就A了,效率还不错。当然作为一个“正直”的ACMer而言,我们不能假设数据是仁慈的,所以用 heap 吧,旱涝保收。
代码:
#include<cstdio>
#include<stack>
#include<climits>
#include<cstring>
using namespace std;
const int N = 30010;
struct Edge{
int s,e,next,v;
}edge[5*N];
int e_num,dist[N],vis[N],head[N];
void AddEdge(int a,int b,int c){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
edge[e_num].next=head[a]; head[a]=e_num++;
}
void spfa(int n){
int i;
stack <int>q;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;dist[i++]=INT_MAX);
q.push(1);vis[1]=1;
dist[1]=0;
while(!q.empty()){
int cur=q.top();
q.pop();
vis[cur]=0;
for(i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(dist[u]-dist[cur]>edge[i].v){
dist[u]=dist[cur]+edge[i].v;
if(!vis[u]){
q.push(u);
vis[u]=1;
}
}
}
}
printf("%d\n",dist[n]);
}
int main()
{
int n,m,a,b,c;
scanf("%d%d",&n,&m);
e_num=0;
memset(head,-1,sizeof(head));
while(m--){
scanf("%d%d%d",&a,&b,&c);
AddEdge(a,b,c);
}
spfa(n);
return 0;
}
POJ3169 Layout
这个题目,对于点上的元素个数,特意给出了说明,可以有任意多个元素在同一个点上,所以不用加边,直接建图求最短路即可。
代码:
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1010;
struct Edge{
int s,e,v,next;
}edge[20*N];
int n,ml,md,e_num,head[N],vis[N],dist[N],countx[N];
void AddEdge(int a,int b,int c){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
edge[e_num].next=head[a]; head[a]=e_num++;
}
void getmap(){
int i,a,b,c;
e_num=0;
memset(head,-1,sizeof(head));
while(ml--){
scanf("%d%d%d",&a,&b,&c);
AddEdge(a,b,c);
}
while(md--){
scanf("%d%d%d",&a,&b,&c);
AddEdge(b,a,-c);
}
for(i=2;i<=n;i++)
AddEdge(i,1,0);
}
void spfa(){
int i;
queue <int> q;
memset(vis,0,sizeof(vis));
memset(countx,0,sizeof(countx));
for(i=1;i<=n;dist[i++]=INT_MAX);
dist[1]=0; vis[1]=1; countx[1]++;
q.push(1);
int tmp=0;
while(!q.empty()){
int cur=q.front();
q.pop();
vis[cur]=0;
if(countx[cur]>n){
tmp=-1;break;
}
for(i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(dist[u]-dist[cur]>edge[i].v){
dist[u]=dist[cur]+edge[i].v;
if(!vis[u]){
q.push(u); vis[u]=1;
countx[u]++;
}
}
}
}
if(tmp==-1)printf("%d\n",tmp);
else printf("%d\n",dist[n]<INT_MAX?dist[n]:-2);
}
int main()
{
scanf("%d%d%d",&n,&ml,&md);
getmap();
spfa();
return 0;
}
POJ1275/ ZOJ1420/ HDU1529 Cashier Employment
黑书上的例题,老经典了,也老难了,我纠结了好久才出来……
这题建图有难度,一般不容易想到所有的约束条件,看了黑书讲解,然后又搜了报告,才做出来的,所以不贴我丑陋的代码了,给个解题报告看看吧,挺不错的。
感谢:
http://hi.baidu.com/accplaystation/blog/item/7c6d10136ef28b856438db6b.html
http://happylch21.blog.163.com/blog/static/165639759201163084924988/