[APIO2017]商旅 0/1分数规划
题解:
upd: 在洛谷上被Hack了。。。思路应该是对的,代码就别看了
感觉有个地方还是非常妙的,就是因为在x买东西,在y卖出,就相当于直接从x走向了y,因为经过中间的城市反正也不会造成任何影响。
所以建图就可以直接把所有城市两两连边,然后直接枚举找出使得在x买东西,在y卖出的利润最大化的物品,并将利润作为权值,(a[i])
再floyd找出x到y的最短路径(这大概就算用到城市可以重复经过的条件了吧)作为代价,(b[i])
接下来就和[HNOI2009]最小圈非常类似了。
只不过这里是要找最大利润,而我们是用spfa找负环,所以可以对边权取相反数,(当然你也可以不变边权找正环)
此外还有一个需要注意的地方,
如果你是二分小数自然没有任何问题,
但是如果你是二分整数就要注意判断0环了(因为答案是向下取整的),因为如果你二分小数的话,反正你会多二分几次来保证精度,所以不判断是没有什么问题的,
但是整数不一样,所以要判断0环,方法也很简单,在dfs版spfa里面的判断条件上加上=就可以了
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 150 5 #define ac 21000 6 #define getchar() *o++ 7 #define inf 2139062143 8 char READ[5001000],*o=READ; 9 int n,m,l,mid,maxn; 10 int buy[AC][AC*10],sale[AC][AC*10],s[AC][AC],f[AC][AC],dis[ac]; 11 int date[ac],Next[ac],Head[AC],length[ac],tot; 12 bool vis[AC],flag; 13 /*想了很久如何处理异地交易的问题,因为中间走过的路程会被忽略, 14 所以实际上就相当于直接从买入地x走到了卖出地y, 15 所以直接两两连边,因为反正可以重复经过,所以走一条边就相当于略过了中间的所有边, 16 然后为了尽量节省时间,所以先处理出任意两点之间的最短路,因为不管怎么走收益不变, 17 但代价变了,所以连边要连最短路,所以先floyd处理一下,再把结果连成边, 18 不过利益不变的前提是买同一种商品,但是商品不止一种,所以还要处理出最大收益, 19 因为地点都已经固定了,所以这个时候就只要枚举一下买哪种划得来了, 20 标准就是买卖单个的收入就行了 21 然后动态改一下权值就好了,dfs版spfa找负环*/ 22 inline int read() 23 { 24 int x=0;char c=getchar();bool z=false; 25 while(c > '9' || c < '0') {if(c == '-') z=true; c=getchar();} 26 while(c >= '0' && c <= '9') x=x*10+c-'0',c=getchar(); 27 if(!z) return x; 28 else return -x; 29 } 30 31 inline void add(int f,int w) 32 { 33 date[++tot]=w,Next[tot]=Head[f],Head[f]=tot; 34 } 35 36 inline void upmin(int &a,int b) 37 { 38 if(b < a) a = b; 39 } 40 41 inline void upmax(int &a,int b) 42 { 43 if(b > a) a = b; 44 } 45 46 void pre() 47 { 48 int a,b,c; 49 n=read(),m=read(),l=read(); 50 memset(f,127,sizeof(f)); 51 for(R i=1;i<=n;i++) 52 { 53 f[i][i]=0; 54 for(R j=1;j<=l;j++) 55 buy[i][j]=read() , sale[i][j]=read(); 56 } 57 for(R i=1;i<=m;i++) 58 { 59 a=read(),b=read(),c=read(); 60 upmin(f[a][b],c);//这里就要upmin。,,,,不然可能会被不优的边覆盖 61 } 62 } 63 64 void init() 65 { 66 /*for(int i=1;i<=n;i++) 67 { 68 for(int j=1;j<=n;j++) 69 printf("%d ",f[i][j]); 70 printf("\n"); 71 } 72 printf("!!!\n");*/ 73 for(R k=1;k<=n;k++) 74 for(R i=1;i<=n;i++) 75 { 76 if(f[i][k] == inf) continue; 77 for(R j=1;j<=n;j++) 78 if(f[k][j] != inf) upmin(f[i][j] , f[i][k] + f[k][j]); 79 }//怕爆了 80 for(R i=1;i<=n;i++)//枚举起点 81 for(R j=1;j<=n;j++)//枚举终点 82 { 83 if(i == j) continue; 84 if(f[i][j] == inf) continue; 85 for(R k=1;k<=l;k++) //枚举买哪种 86 { 87 if(sale[j][k] == -1 || buy[i][k] == -1) continue; 88 upmax(s[i][j],sale[j][k] - buy[i][k]);//获取利润 89 upmax(maxn,s[i][j] / f[i][j]); 90 } 91 } 92 /*for(int i=1;i<=n;i++) 93 { 94 for(int j=1;j<=n;j++) 95 printf("%d ",f[i][j]); 96 printf("\n"); 97 } 98 // printf("---------------\n"); 99 printf("\n"); 100 for(int i=1;i<=n;i++) 101 { 102 for(int j=1;j<=n;j++) 103 printf("%d ",s[i][j]); 104 printf("\n"); 105 }*/ 106 for(R i=1;i<=n;i++) 107 for(R j=1;j<=n;j++) 108 { 109 if(i == j) continue; 110 if(f[i][j] == inf) continue;//error,,,这里不能加上!s[i][j],虽然没有利润,但仍然可以起到连接作用 111 add(i,j); 112 } 113 } 114 115 void spfa(int x) 116 { 117 int now; 118 vis[x]=true; 119 for(R i=Head[x]; i ;i=Next[i]) 120 { 121 now=date[i]; 122 if(dis[now] >= dis[x] + length[i])//找负环肯定是最短路啊 123 {//加个等号判0环? 124 if(vis[now]) {flag=true;break;} 125 dis[now] = dis[x] + length[i]; 126 spfa(now); 127 } 128 } 129 vis[x]=false; 130 } 131 132 bool check() 133 { 134 int now; 135 flag=false; 136 memset(dis,0,sizeof(dis)); 137 for(R i=1;i<=n;i++) 138 for(R j=Head[i]; j ;j=Next[j]) 139 { 140 now=date[j]; 141 length[j]=mid * f[i][now] - s[i][now];//因为要用找负环的方法找正环,所以边权取相反 142 }//error,,是f[i][now] 143 for(R i=1;i<=n;i++) 144 { 145 spfa(i); 146 if(flag) return true; 147 } 148 return false; 149 } 150 151 void half() 152 { 153 int l=0,r=maxn; 154 while(r - l > 1) 155 { 156 mid=(l + r) / 2; 157 if(check()) l=mid; 158 else r=mid; 159 } 160 if(r > l) 161 { 162 mid=r; 163 if(check()) printf("%d\n",r); 164 else printf("%d\n",l); 165 } 166 else printf("%d\n",l); 167 } 168 169 int main() 170 { 171 // freopen("in.in","r",stdin); 172 fread(READ,1,5000000,stdin); 173 pre(); 174 init(); 175 half(); 176 // fclose(stdin); 177 return 0; 178 }