1.写对不等式是关键。不仅是题中给出的显性的不等关系,还有诸如b>a等隐形的关系。不管重不重要,不等式一定要写全。
2.对于一些题,需要加入超级源点,并将该点与其他每一点连一条权值都相同(一般选0)的有向边。
3.注意最后的输出,到底是f[n],还是f[n+1],还是f[1]。若存在负环,则无最短路,存在正环,则无最长路。对于多组数据,要注意加边的结构体的数组大小(是否太小而导致re)。
poj 1364
样例输入:
4 2
1 2 gt 0
2 2 lt 2
1 2 gt 0
1 0 lt 0
0
样例输出:
lamentable kingdom
successful conspiracy
思路:
这道题的源点选错了。由于每个点都会使用,所以应该开一个超级源点,并将该点与其他的点连上权值为
k的有向边。并且由于不等式的不等号为严格的大于或小于,所以连边时应将权值加上1才能表示大于等于
或小于等于。
以后对于这种题,都可以开一个超级源点,将该点与其他的点连权值相同的有向边(一般选取0),然后
判断是否有环(即in[u]>n)。最长路有正环则无解,最短路有负环则无解。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int n,m;
int head[110],vis[110],f[110],in[110];
struct edge
{
int v,w,next;
}e[100000];
void init()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
}
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
memset(f,-0x3f3f3f,sizeof(f));
queue<int>q;
f[n+1]=0;
vis[n+1]=1;
in[n+1]=1;
q.push(n+1);
int ok=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
if(in[u]>n)
{
ok=1;
break;
}
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w>f[v])
{
f[v]=f[u]+e[i].w;
//printf("***%d %d %d\n",u,f[v],v);
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
in[v]++;
}
}
}
}
if(ok==1)printf("successful conspiracy\n");
if(ok==0)printf("lamentable kingdom\n");
}
void read()
{
while(scanf("%d",&n)==1)
{
if(n==0)break;
scanf("%d",&m);
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int a,b,c;
char x,y;
scanf("%d%d",&a,&b);
scanf(" %c%c",&x,&y);
if(x=='g'&&y=='t')//大于
{
scanf("%d",&c);
adde(a-1,a+b,c+1);
}
if(x=='l'&&y=='t')//小于
{
scanf("%d",&c);
adde(a+b,a-1,1-c);
}
}
for(int i=0;i<=n;i++)
{
adde(n+1,i,0);
}
work();
}
}
int main()
{
init();
read();
return 0;
}
poj 3159
思路:
这道题我开始用spfa+队列来做就超时了,然后看某些大神的题解才知道用spfa+栈不会超时。
这道题好像是2008年四川的省选题。原题应该是考的是Dijkstra+堆。但是我还是写的是spfa+栈。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<stack>
using namespace std;
#define maxn 30000+10
int n,m;
int head[maxn],vis[maxn],f[maxn];
struct edge
{
int v,next,w;
}e[150000+10];
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
stack<int>q;
q.push(1);
vis[1]=1;
f[1]=0;
while(!q.empty())
{
int u=q.top();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w<f[v])
{
f[v]=f[u]+e[i].w;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
printf("%d",f[n]);
}
int main()
{
memset(head,-1,sizeof(head));
memset(f,0x3f3f3f,sizeof(f));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
adde(a,b,c);
}
work();
return 0;
}
poj 2983
思路:
这道题对于精确的等式有一个巧妙地处理。
对于一个等式a-b=c。若要将它化为不等式,可以转化为两个不等式,即:
a-b>=c&&a-b<=c,我们对于这两个不等式求交集的话,就是a-b=c,所以这两者等价。
所以在读到精确信息之后,可以加一条b到a权值为c的边,和一条a到b权值为-c的边。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define maxn 1000+3
int n,m;
int head[maxn],vis[maxn],f[maxn],in[maxn];
struct node
{
int v,w,next;
}e[5000000+10];
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
memset(f,-0x3f3f3f,sizeof(f));
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
int ok=1;
queue<int>q;
q.push(0);
f[0]=0;
vis[0]=1;
in[0]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
if(in[u]>n)
{
printf("Unreliable\n");
return ;
}
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w>f[v])
{
f[v]=f[u]+e[i].w;
if(vis[v]==0)
{
in[v]++;
vis[v]=1;
q.push(v);
}
}
}
}
printf("Reliable\n");
}
void read()
{
while(scanf("%d%d",&n,&m)==2)
{
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
scanf("\n");
int a,b,c;
char x;
scanf("%c",&x);
if(x=='P')
{
scanf("%d%d%d",&a,&b,&c);
adde(b,a,c);
adde(a,b,-c);
}
if(x=='V')
{
scanf("%d%d",&a,&b);
adde(b,a,1);
}
}
for(int i=1;i<=n;i++)
{
adde(0,i,0);
}
work();
}
}
int main()
{
read();
return 0;
}
poj 3169
思路:
这道题的不等式很容易得到
f[b]-f[a]>=c;
f[b]-f[a]<=c;___________f[a]-f[b]>=-c
我个人是将加边反着加的,这样就可以从1到n跑一次spfa
注意用in记录每个点入队的次数,in[u]大于n的话,就无解,输出-1;
如果f[n]没有更新的话,说明前面的那些点的条件对它没有限制,那么就应有无数解,输出-2;
其他的就输出f[n]即可。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int n,m1,m2;
int head[1001],vis[1001],f[1001],in[1001];
struct node
{
int v,w,next;
}e[1000000];
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
queue<int>q;
q.push(1);
vis[1]=1;
f[1]=0;
in[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
if(in[u]>n)
{
printf("-1");
return ;
}
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w<f[v])
{
f[v]=f[u]+e[i].w;
if(vis[v]==0)
{
in[v]++;
vis[v]=1;
q.push(v);
}
}
}
}
if(f[n]==0x3f3f3f3f)
{
printf("-2");
return ;
}
printf("%d",f[n]);
}
int main()
{
memset(head,-1,sizeof(head));
memset(f,0x3f3f3f,sizeof(f));
scanf("%d%d%d",&n,&m1,&m2);
int a,b,c;
for(int i=1;i<=m1;i++)
{
scanf("%d%d%d",&a,&b,&c);
adde(a,b,c);
}
for(int i=1;i<=m2;i++)
{
scanf("%d%d%d",&a,&b,&c);
adde(b,a,-c);
}
work();
return 0;
}
poj 1201
思路:
由于太水而直接上代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define maxn 50000+3
int n;
int head[maxn],vis[maxn],f[maxn];
int min_num=0x3f3f3f,max_num=-1;
struct adge
{
int v,next,w;
}e[4*maxn];
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
queue<int>q;
q.push(min_num);
f[min_num]=0;
vis[min_num]=1;
while(!q.empty())
{
int u=q.front();
//printf("***%d\n",u);
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w>f[v])
{
f[v]=f[u]+e[i].w;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
printf("%d",f[max_num]);
}
int main()
{
memset(head,-1,sizeof(head));
memset(f,-0x3f3f3f,sizeof(f));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
min_num=min(min_num,a);
max_num=max(max_num,b+1);
adde(a,b+1,c);
}
for(int i=min_num;i<max_num;i++)
{
adde(i+1,i,-1);
adde(i,i+1,0);
}
work();
return 0;
}
poj 1716
思路:
尼玛的处理边界。
其实不等式还是很好推,就是这三个
s[b+1]-s[a]>=2;
s[i+1]-s[i]>=0;
s[i]-s[i-1]>=-1;
后面两个不等式是根据题目中b>a推出的。
就是最后的源点的选取和与处理的边界。
源点选0,那么边界就为[1,max_num+1];
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define maxn 10000+10
int n,max_num=-1;
int head[maxn],vis[maxn],f[maxn];
struct node
{
int v,next,w;
}e[1000000];
int k=1;
void adde(int u,int v,int w)
{
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
void work()
{
memset(f,-0x3f3f3f,sizeof(f));
queue<int>q;
q.push(0);
vis[0]=1;
f[0]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(f[u]+e[i].w>f[v])
{
f[v]=f[u]+e[i].w;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
printf("%d",f[max_num]);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
adde(a,b+1,2);
max_num=max(max_num,b+1);
}
for(int i=0;i<=max_num;i++)
{
adde(i,i+1,0);
adde(i+1,i,-1);
}
work();
return 0;
}
poj 1275
由于太难而直接上代码:
#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
using namespace std;
#define inf 0x7fffffff
#define M 25
struct edge{
int v, w, next;
}e[5*M];
bool inq[M];
int dist[M], pre[M], n = 24, cnt, ind[M];
void init ()
{
memset (pre, -1, sizeof(pre)); cnt = 0;
}
void addedge (int u, int v, int w)
{
e[cnt].v = v; e[cnt].w = w; e[cnt].next = pre[u]; pre[u] = cnt++;
}
bool spfa (int u)
{
int i, v, w;
for (i = 0; i <= n; i++)
{
dist[i] = -inf;
inq[i] = false;
ind[i] = 0;
}
queue<int> q;
q.push (u);
inq[u] = true;
dist[u] = 0;
while (!q.empty())
{
u = q.front();
if (++ind[u] > n)
return false;
q.pop();
inq[u] = false;
for (i = pre[u]; i != -1; i = e[i].next)
{
v = e[i].v;
w = e[i].w;
if (dist[u] + w > dist[v])
{
dist[v] = dist[u] + w;
if (!inq[v])
{
q.push (v);
inq[v] = true;
}
}
}
}
return true;
}
int main()
{
int t, R[M], num[M], i, pos, m, l, r, ans;
scanf ("%d", &t);
while (t--)
{
for (i = 1; i <= n; i++)
scanf ("%d", R+i);
scanf ("%d", &m);
memset (num, 0, sizeof(num));
for (i = 0; i < m; i++)
{
scanf ("%d", &pos);
num[pos+1]++;
}
l = 0, r = m;
bool flag = true;
while (l < r)
{
init();
ans = (l+r) / 2;
for (i = 1; i <= n; i++)
{
addedge (i-1, i, 0);
addedge (i, i-1, -num[i]);
}
for (i = 8; i <= n; i++)
addedge (i-8, i, R[i]);
for (i = 1; i < 8; i++)
addedge (i+16, i, R[i]-ans);
addedge (0, 24, ans);
if (spfa (0))
r = ans, flag = false;
else l = ans + 1;
}
if (flag)
puts ("No Solution");
else printf ("%d\n", r);
}
return 0;
}