差分約束小結(poj1201,poj1716,poj1364,poj3159,poj3169,poj1275)
總體感覺,難點是建圖,因為建圖的時候需要考慮一些題目上沒有明確給出的隱含條件,只有把所有約束關係找全之後,然後再正確運用最短路(或者最長路)的性質求解,才能得到正確答案。
說說我的收穫:
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 。這就是上面說的隱含條件的應用。
這個題是上面那個的閹割版,把每個區間端點的大小關係設為了定值,其他的一樣。
#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; }
詳見結題報告
這個題不卡建圖,所有約束關係都給你了,只要根據給的不等式建邊就好,不過,用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
黑書上的例題,老經典了,也老難了,我糾結了好久才出來……
這題建圖有難度,一般不容易想到所有的約束條件,看了黑書講解,然後又搜了報告,才做出來的,所以不貼我醜陋的程式碼了,給個解題報告看看吧,挺不錯的。
感謝: