poj 3621(参数搜索 + spfa)

本文介绍了一种利用二分法解决整数规划问题的方法,特别针对形式为(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)的问题,其中变量xi只能取0或1。通过转换为不等式判断的形式,并结合SPFA算法检测负权回路,实现了对最优解的逼近。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

01整数规划问题就是求解方程(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)的最小值/最大值问题。其中xi = 0或1(i=1,2...n)

对于此类问题我们可以通过二分来求解很接近答案的近似值。我们可以先令:
(a1*x1+a2*x2+..+an*xn)/(b1*x1+b2*x2+..+bn*xn)=L,则我们可以将此式转换为:x1*(a1-b1*L)+x2*(a2-b2*L)+...xn*(an-bn*L)=0,我们先定义一个估计值val,如果这个值使得上面的式子小于0我们就可以知道val>L,如果上式等于0,则val = L;如果大于0,则val<L,显然我们可以采用二分的思想求解次问题。

对于POJ 3621 假设存在边uv,点u和点v的欢乐值分别为happy[u]和happy[v]。u到v的花费为cost[u][v]。则我们可以构造一个新图,这个新图的边变为:
happy[v]-val*cost[u][v](val为估计值),然后我们采用SPFA求解此图是否存在负环。如果存在负环。假设此环所有点为y1,y2.....ym,则满足下面式子:
(cost[1][2]*val-happ[2])+(cost[2][3]*val-happy[3])+....+(cost[m][1]-happ[1]) < 0
显然val比最优解还要小,此时我们可以增大val,反之,如果不存在负环,则我们需要减少val(对于a1和b1*L在符号中的先后关系需要看题目是求最大值还是最小值,此题是把b1*L放在符号前面的)。如此进行二分求解,知道满足题目要求的精度就可以终止了。 对于POJ 2728 则可以使用类似的方法求解.

 讲解转自 http://hi.baidu.com/ofeitian/blog/item/ee15253e4f0f4dce7c1e7123.html 


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const double inf=1000000000000.00;
const int N=5010,M=1010;
#define cc(m,v) memset(m,v,sizeof(m))
struct node{
    int v,next,w;
}edge[N];
int head[M],p,vis[M],num[M],sum[M];
double dis[M];
queue<int> que;
void ainit(){
    p=0,cc(head,-1);
}
void addedge(int u,int v,int w){
    edge[p].v=v,edge[p].w=w,edge[p].next=head[u],head[u]=p++;
}
bool spfa(double val,int n){
    int i,u,v;
    double w;
    for(i=0;i<=n;i++)  dis[i]=inf;
    cc(vis,0),cc(sum,0);
    que.push(1),vis[1]=1,dis[1]=0;
    while(!que.empty()){
        u=que.front(),que.pop();
        vis[u]=0;
        for(i=head[u];i!=-1;i=edge[i].next){
            w=(double)val*edge[i].w-num[v=edge[i].v];
            if(dis[v]>w+dis[u]){
                dis[v]=w+dis[u];
                sum[v]++;
                if(sum[v]>=n) return 0;
                if(!vis[v])
                    que.push(v),vis[v]=1;
            }
        }
    }
    return 1;
}
int main(){
    int n,m,u,v,i,w;
    double rig,lef,mid,ans;
    while(scanf("%d%d",&n,&m)!=-1){
        ainit();
        for(i=1;i<=n;i++)
            scanf("%d",&num[i]);
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        lef=0,rig=1000;
        while(rig-lef>0.0001){
            mid=(lef+rig)/2;
            if(spfa(mid,n)) ans=mid,rig=mid;
            else lef=mid;
        }
        printf("%.2lf\n",ans);
    }
    return 0;
}


``` #define _CRT_SECURE_NO_WARNINGS #include <algorithm> #include <cstdio> #include <vector> #include <queue> #include<iostream> #define N 1005 using namespace std; int ti[24], n; vector<int>num(24, 0); int head[N], to[N * 24], w[N * 24], nex[N * 24], cnt; void add(int a, int b, int m) { to[++cnt] = b, w[cnt] = m, nex[cnt] = head[a], head[a] = cnt; } void build(int nm) { for (int i = 1; i < 24; i++) { add(i, i - 1, -num[i]); add(i - 1, i, 0); } for (int i = 8; i < 24; i++) add(i - 8, i, ti[i]); for (int i = 0; i < 8; i++) add(i + 16, i, ti[i] - nm); for (int i = 0; i < 24; i++) add(24, i, 0); } bool SPFA(int mn) { build(mn); vector<bool>vis(25, false); vector<int>dis(25, -1000000000); vector<int>cont(25, 0); dis[24] = 0; queue<int>line; line.push(24); while (line.size()) { auto a = line.front(); line.pop(); vis[a] = false; for (int i = head[a]; i; i = nex[i]) { int v = to[i]; if (dis[v] < dis[a] + w[i]) { dis[v] = dis[a] + w[i]; if (!vis[v]) { cont[v]++; line.push(v); vis[v] = 1; } if (cont[v] > 25) { return false; } } } } return true; } int main() { int t, l, r; cin >> t; int temp[1000],top=0; while (t--) { for (int i = 0; i < 24; i++) cin >> ti[i]; int ans = -1; cin >> n; for (int i = 0; i < n; i++) { int a; cin >> a; num[a]++; } l = 0, r = n; while (l <= r) { int mid = (r + l) / 2; cnt = 0; if (SPFA(mid)) { r = mid - 1; ans = mid; } else l = mid + 1; memset(head, 0, sizeof(head)); memset(to, 0, sizeof(to)); memset(w, 0, sizeof(w)); memset(nex, 0, sizeof(nex)); } temp[top++] = ans; //if (ans == -1)cout << "No Solution" << endl; //else cout << ans << endl; num.assign(24,0); } for (int i = 0; i < top; i++)cout << temp[i] << endl; }```为什么对这个数据就会错误 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 0 8 16 16
最新发布
04-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值