图论——负环与01分数规划
Sightseeing Cows
思路:
01分数规划可以考虑二分答案,先判断边界的范围是(0,1000 ]
再来看二分的过程。
关键就在于判断是否存在一个环点权之和/边权之和>mid,此时答案在右半区间,否则答案在左半区间。
整理一下式子会发现
因为本题里既有点权又有边权,我们可以考虑把每个点的点权放到出边上。即
即把每条边的权重看作是f[i] - mid * t[i] ,问题即转化为图中是否存在正环。
思想跟求负环一样的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
const double eps=1e-4;
int n,m;
int wf[maxn];
int h[maxn],e[maxn],wt[maxn],ne[maxn],idx;
double d[maxn];
int q[maxn],cnt[maxn];
bool st[maxn];
void add(int a,int b,int c){
e[idx]=b,wt[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool check(double mid){
memset(d,0,sizeof d);
memset(st,0,sizeof st);
memset(cnt,0,sizeof cnt);
queue<int>q;
for(int i=1;i<=n;i++){
q.push(i);
st[i]=1;
}
while(!q.empty()){
int t=q.front();
q.pop();
st[t]=0;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(d[j]<d[t]+wf[t]-mid*wt[i]){
d[j]=d[t]+wf[t]-mid*wt[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return 1;
if(!st[j]){
st[j]=1;
q.push(j);
}
}
}
}
return 0;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>wf[i];
memset(h,-1,sizeof h);
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);//单向边
}
double l=0,r=1e6;
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.2f\n",l);
return 0;
}
Word Rings (建图的特殊性)
思路:
也是一道01分数规划问题,但关键在于如何建图。
因为匹配的时候只是考虑首尾两个字母,对于一个字符串我们可以分别以首尾两个字母为起点和终点,字符串长度为边的权重建图。
比如ababc,就可以建ab->bc 的长度为5的边。
建图完成后,再看如何求解该问题。问题转化为求所有的边权之和/1最大值。
先看二分的范围是(0,1000 ]
跟上题类似:当
答案在右半区间,反之在左半区间。
可以重新定义边权为w[i]-mid,看图中是否存在正环。
优化可以用之前提到的把SPFA的队列换成栈,真玄学优化。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 700, M = 100010;
int n;
int h[maxn], e[M], w[M], ne[M], idx;
double d[maxn];
int q[maxn], cnt[maxn];
bool st[maxn];
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool check(double mid){
memset(st,0,sizeof st);
memset(cnt,0,sizeof cnt);
int hh=0,tt=0;
for(int i=0;i<676;i++){
q[tt++]=i;
st[i]=1;
}
int count=0;
while(hh!=tt){
int t=q[hh++];
if(hh==maxn) hh=0;
st[t]=0;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(d[j]<d[t]+w[i]-mid){
d[j]=d[t]+w[i]-mid;
cnt[j]=cnt[t]+1;
if(++count>10000) return 1;
if(cnt[j]>=700) return 1;
if(!st[j]){
st[j]=1;
q[tt++]=j;
if(tt==maxn) tt=0;
}
}
}
}
return 0;
}
int main(){
char str[1100];
while(scanf("%d",&n),n){
memset(h,-1,sizeof h);
idx=0;
for(int i=0;i<n;i++){
scanf("%s",str);
int len=strlen(str);
if(len>=2){
int left=(str[0]-'a')*26+str[1]-'a';
int right=(str[len-2]-'a')*26+str[len-1]-'a';
add(left,right,len);
}
}
if(!check(0)) puts("No solution");
else{
double l=0,r=1000;
while(r-l>1e-4){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.10f\n",r);
}
}
return 0;
}

京公网安备 11010502036488号