Fork me on GitHub

POJ2240-Arbitrage

继续刷邝斌飞最短路专题

POJ2240(Time Limit:1000MS、Memory Limit:65536K)

洛谷(时间限制:242ms、内存限制:1.46GB,tmd惊到我了)  ——  洛谷买一送一(时间限制:3s)

HDU(Time Limit:1000 MS、Memory Limit:32768K

可用平台(总时间限制:1000ms 内存限制:65536kB)

风格好爱,很有梯度,深入浅出。

之前搜索专题里着火问题重复出现了、倒水问题重复出现了,这里汇率套利问题重复出现了

读完题起手就写

(我第二次刷这个题居然要回头看自己写的博客,而第一次刷汇率的时候,完完全全自己想出来的,真的是题目介绍越详细越好,这个题有很多细节没说,那个题说了很多看似没用,但其实很有提示的话)

理解了邝斌说的人一我十,人十我百,我是真的笨,那个题我是用自己探索出来的不能二次加入来区分迪杰斯特拉和贝尔曼的,并没考虑自增正数环的问题(因为那个题每个兑换点都是能兑换两种,我误以为不会出现环,这也是我那篇文章后来更新的内容,其实只要多一个兑换点就可以有环(针对那个题的样例),而此时我也才理解自己那篇博客里用来对拍的别人的AC博客,尽管自己什么都没看就AC掉,却现在才理解那个题别人的题解),而读完这题我想到了环的问题,真的是刷了这么多自以为的难题,但也只是稍微了解了下最短路算法!!但其实也并不简单吧,那个不配我刷的能看数据的(我记得是)垃圾leetcode里(纯纯商业oj,居然还有开会员功能,真垃圾,CCF和浙大导师陈越姥姥的PAT虽然很有难度但也是是商业的玩意),估计这都属于hard

 

别人可以一道题想明白所有,举一反三,我一个题只能想懂这个题的东西,很多细节点都还是很朦胧

 

1 也理解克拉丽丝“这不是BZOJ原题吗.jpg”,“虽然这题我不会,但AC还是没有问题的”,或许是鏼神说的。相册里,宾馆里克拉丽丝给区域金q神讲题,q神拖托下巴
2 
3 王泽姜贺良。图库里第四范式戴牛戴文渊蹲着给女实习生写代码找bug
4 
5 北师数系q神和岛娘打游戏空间抱怨被他们各自qq好友朋友拉黑
View Code

但读懂那个贝尔曼AC博客后,发现那人的输入挺巧妙,除此之外我发现我和那人代码都有相当大的问题:

1、他的代码思路是只要有正环,即自增,那就认为是最后可以比本金大,但如果我有一个正环,但兑换回我持有的货币种类的时候,一路兑换回去全是汇率很小的,不完犊子了么?!

例子:

20 19 1 1000
1 2 0.01 0 0.01 0
2 3 0.01 0 0.01 0
3 4 0.01 0 0.01 0
4 5 0.01 0 0.01 0
5 6 0.01 0 0.01 0
6 7 0.01 0 0.01 0
7 8 0.01 0 0.01 0
8 9 0.01 0 0.01 0
9 10 0.01 0 0.01 0
10 11 0.01 0 0.01 0
11 12 0.01 0 0.01 0
12 13 0.01 0 0.01 0
13 14 0.01 0 0.01 0
14 15 0.01 0 0.01 0
15 16 0.01 0 0.01 0
16 17 0.01 0 0.01 0
17 18 0.01 0 0.01 0
18 19 0.01 0 0.01 0
19 20 100 0 100 0
View Code

看图也行

 一路过去只有最后一个兑换点19 20的时候是自增的,你这题AC了是int,范围最大19位,我自增到无限大也是最多19位,但我兑换回去的路上,18、17...1,都是汇率为0.01,一次就少2位,10次就tm少20位,那怎么可能比本金大啊,于是我举了个我这个的例个,想通过我的代码输出NO,他的代码输出YES,来推翻,但当我把这个数据输进去的时候,发现我的代码也是YES

2、关于我的代码,他是怎么YES相当离奇,其中出现了1000>1000的东西,但我控制变量逐一调试半天发现,其实是自增超范围了,无限增加超过int这也能理解,但溢出后变为极小的数还能大于本金1000,见代码76行开始的那一段

#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#include<iostream>
using namespace std;//vector必须要有这句
struct Edge{
    int to;
    double rate;
    double commission;
} edge;
vector<Edge>G[101];//最多100种货币
queue<int>q;
double D[101];
int flag;
void SPFA();
int N,M,S;
double V;//money
int a,b;
int main()
{
    freopen("zhishu.txt","r",stdin);
    double rate_ab, commission_ab;
    double rate_ba, commissiom_ba;
    while(cin>>N>>M>>S>>V){
        while(!q.empty())
            q.pop();
        flag=0;
        memset(D,0,sizeof(D));
        for(int i=0;i<M;i++){
            cin>>a>>b>>rate_ab>>commission_ab>>rate_ba>>commissiom_ba;
            edge.to=b;
            edge.rate=rate_ab;
            edge.commission=commission_ab;
            G[a].push_back(edge);

            edge.to=a;
            edge.rate=rate_ba;
            edge.commission=commissiom_ba;
            G[b].push_back(edge);
        }//至此存图结束

        SPFA();

        if(flag==1)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;

    }
}
void SPFA()
{
    q.push(S);
    D[S]=V;

     if(D[S]>V)
        cout<<"!"<<endl;

    int cnt=0;
    while(!q.empty()){
        int u;
        u=q.front();
        q.pop();
        cnt++;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].to;
            double rate=G[u][i].rate;
            double commission=G[u][i].commission;
            if(v==S)
                cnt++;
             if(cnt==M*10000){
                return;
            }

            if(D[1]>V){
                cout<<"D[1]-V: "<<D[1]-1000<<endl;
                cout<<1.13687e-12+1000<<endl;
                cout<<"D[1]:"<<D[1]<<endl;
                cout<<"V:"<<V<<endl;
//                if(1000>V)
//                    cout<<"$"<<endl;
                if(D[1]>1000)
                    cout<<"@"<<D[1]<<endl;

                flag=1;
                return;
            }
//            cout<<D[v]<<endl;
            if((D[u]-commission)*rate>D[v]){
                D[v]=(D[u]-commission)*rate;
                q.push(v);
            }
        }
    }
}
//20 19 1 1000
//1 2 0.01 0 0.01 0
//2 3 0.01 0 0.01 0
//3 4 0.01 0 0.01 0
//4 5 0.01 0 0.01 0
//5 6 0.01 0 0.01 0
//6 7 0.01 0 0.01 0
//7 8 0.01 0 0.01 0
//8 9 0.01 0 0.01 0
//9 10 0.01 0 0.01 0
//10 11 0.01 0 0.01 0
//11 12 0.01 0 0.01 0
//12 13 0.01 0 0.01 0
//13 14 0.01 0 0.01 0
//14 15 0.01 0 0.01 0
//15 16 0.01 0 0.01 0
//16 17 0.01 0 0.01 0
//17 18 0.01 0 0.01 0
//18 19 0.01 0 0.01 0
//19 20 100 0 100 0
View Code

经过实验发现,D[1]是1.13687e-12+1000,但

你单独cout他,就是1000,

你用他和1000作比较,还大于1000,

他减去1000后再cout是1.13687e-12

他加上1000后再cout是2000

所以我觉得怎么搞都不对啊,先搁置不管了

姑且认为样例没有这种会干溢出的情况

至此我也猜到了那个看不懂意思的英文限制范围10^4应该就是防止这里溢出的,而这个题没有这个限制,我估计SPFA要凉凉,贝尔曼才行,先写SPFA

那我重新把那个题写下,如果只是判断负环的话,就应该n个点都加入队列,

但我又进一步理解贝尔曼了,之前这博客里的高潮博客说 因为如果一个图有V个顶点,那图中路径最多也只经过V-1条边 ,就他举那个例子,如果只有024三个点,那02是6,24是5,42如果是-10,那最短不就是0242,啊不对成环了。那也就是说,n个点,起点最多经历其他n-1个点后成为最短路径,与单向还是双向的边无关。n-1虽说是点数减一,仿佛跟点有关,其实理解成迭代次数更好,“遍历m条边”这个操作迭代搞n-1次

所以那个题AC代码里其实我用的是顶点S更新次数做判断,即 if(cnt==M*10000) ,是把在高汇率兑换点那循环此时考虑了进来,一直干到比本金大,但其实发现没必要,参考我之前虫洞博客里提到的两个洛谷题解(ljcljc wjy666),其实就是n个点都加入队列找自增正环(任意点进入超过n-1次即是),但要跟贝尔曼算法的判断正环区别开(贝尔曼n*m结束后再来一次遍历所有边,发生更新即有正环)

那我再写下那个题更新更透彻的SPFA,之前写法有点跟贝尔曼判断负环混淆了

些许坎坷直接AC

AC代码

 1 #include<stdio.h>
 2 #include<vector>
 3 #include<queue>
 4 #include<string.h>
 5 #include<iostream>
 6 using namespace std;//vector必须要有这句
 7 struct Edge{
 8     int to;
 9     double rate;
10     double commission;
11 } edge;
12     int cnt[101];
13 vector<Edge>G[101];//最多100种货币
14 queue<int>q;
15 double D[101];
16 int flag;
17 void SPFA();
18 int N,M,S;
19 double V;//money
20 int a,b;
21 int main()
22 {
23 //    freopen("zhishu.txt","r",stdin);
24     double rate_ab, commission_ab;
25     double rate_ba, commissiom_ba;
26     while(cin>>N>>M>>S>>V){
27         while(!q.empty())
28             q.pop();
29         flag=0;
30         memset(D,0,sizeof(D));
31         memset(cnt,0,sizeof(cnt));
32         for(int i=0;i<M;i++){
33             cin>>a>>b>>rate_ab>>commission_ab>>rate_ba>>commissiom_ba;
34             edge.to=b;
35             edge.rate=rate_ab;
36             edge.commission=commission_ab;
37 //            edge.cnt=0;
38             G[a].push_back(edge);
39             q.push(a);
40 
41             edge.to=a;
42             edge.rate=rate_ba;
43             edge.commission=commissiom_ba;
44 //            edge.cnt=0;
45             G[b].push_back(edge);
46             q.push(b);
47         }//至此存图结束
48 
49         SPFA();
50 
51         if(flag==1)
52             cout<<"YES"<<endl;
53         else
54             cout<<"NO"<<endl;
55 
56     }
57 }
58 void SPFA()
59 {
60     D[S]=V;
61     while(!q.empty()){
62         int u;
63         u=q.front();
64 //        cout<<u<<endl;
65         q.pop();
66         for(int i=0;i<G[u].size();i++){
67             int v=G[u][i].to;
68             double rate=G[u][i].rate;
69             double commission=G[u][i].commission;
70 //cout<<(D[u]-commission)*rate<<" "<<D[v]<<endl;
71             if((D[u]-commission)*rate>D[v]){
72                 D[v]=(D[u]-commission)*rate;
73 //                cout<<D[v]<<endl;
74                 cnt[v]++;
75                 if(cnt[v]>N-1){
76                     flag=1;
77                     return;
78                 }
79                 q.push(v);
80             }
81         }
82     }
83 }
View Code

但我又发现,D的类型是double啊,最大值是1.79e+308,加一的话还是1.79e+308因为科学计数法表示的,我用(1.79e+308)+(1e+308)发现是输出inf,但我cout inf,tmd告诉我inf没定义,艹了,math和float头文件都不是找不到头文件。我本意是想加一个判断,  if(((D[u]-commission)*rate)<0) continue; 但并不确定double溢出了是负数,实验发现是inf。那为啥会出现上面说的离奇的情况??但如果范围是e308,那最多100个货币,挨个回去乘0.01,也就是变成了e108,本金最大1000,所以还是会YES,即只要有正环自增,就行。至此我确定题目数据只要有正自增环,即可,因为本金最大1000,如果本金最大可以是109位数字就不行了,但现在唯一疑惑点是为何我那个数据输出本金是1.13687e-12+1000,经过测试发现仿佛发现了不得了的事情,八成是无穷大的科学计数法在计算机中表现形式有什么我不知道的知识点吧,放个结论,测试数据依旧是

20 19 1 1000
1 2 0.01 0 0.01 0
2 3 0.01 0 0.01 0
3 4 0.01 0 0.01 0
4 5 0.01 0 0.01 0
5 6 0.01 0 0.01 0
6 7 0.01 0 0.01 0
7 8 0.01 0 0.01 0
8 9 0.01 0 0.01 0
9 10 0.01 0 0.01 0
10 11 0.01 0 0.01 0
11 12 0.01 0 0.01 0
12 13 0.01 0 0.01 0
13 14 0.01 0 0.01 0
14 15 0.01 0 0.01 0
15 16 0.01 0 0.01 0
16 17 0.01 0 0.01 0
17 18 0.01 0 0.01 0
18 19 0.01 0 0.01 0
19 20 100 0 100 0
View Code

我的代码是

#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
#include<iostream>
using namespace std;//vector必须要有这句
struct Edge{
    int to;
    double rate;
    double commission;
} edge;
vector<Edge>G[101];//最多100种货币
queue<int>q;
double D[101];
int flag;
void SPFA();
int N,M,S;
double V;//money
int a,b;
int main()
{
//    freopen("zhishu.txt","r",stdin);
    double rate_ab, commission_ab;
    double rate_ba, commissiom_ba;
    while(cin>>N>>M>>S>>V){
        while(!q.empty())
            q.pop();
        flag=0;
        memset(D,0,sizeof(D));
        for(int i=0;i<M;i++){
            cin>>a>>b>>rate_ab>>commission_ab>>rate_ba>>commissiom_ba;
            edge.to=b;
            edge.rate=rate_ab;
            edge.commission=commission_ab;
            G[a].push_back(edge);

            edge.to=a;
            edge.rate=rate_ba;
            edge.commission=commissiom_ba;
            G[b].push_back(edge);
        }//至此存图结束

        SPFA();

        if(flag==1)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;

    }
}
void SPFA()
{
    q.push(S);
    D[S]=V;
//    cout<<D[S]-V<<endl;
    int cnt=0;
    while(!q.empty()){
        int u;
        u=q.front();
        q.pop();
        cnt++;
        for(int i=0;i<G[u].size();i++){
            int v=G[u][i].to;
            double rate=G[u][i].rate;
            double commission=G[u][i].commission;
            if(v==S)
                cnt++;
            if(cnt==M*90390){
                return;
            }

            if(D[S]>V){
//                cout<<"#"<<D[S]-1000<<endl;
                flag=1;
                return;
            }
//            cout<<D[v]<<endl;
            if((D[u]-commission)*rate>D[v]){
                D[v]=(D[u]-commission)*rate;
                q.push(v);
                    cout<<"^"<<D[v]<<"    "<<u<<" "<<v<<endl;
//                    cout<<"!"<<D[v]-0.1<<endl;
                if(v==19)
                    cout<<"——"<<D[v]-(1e-29)<<endl;
                    cout<<endl;
//                    if(v==2)
//                        cout<<"#"<<D[v]-100000<<" "<<D[v]<<endl;
            }
        }
    }
}
View Code

我发现的结果是123顺序往下兑换的时候没问题,但回来的时候,即通过19和20兑换点循环增大的时候,如果flag=1后没有return,是可以判断出确实比1000本金大的,但加return后发现,兑换回本金1号货币还是1000,我测试发现从3兑换回2是10^5,但用他减去10^5实际也是有个很小的数,而第一次从20到19数变大了,是1e-29,但用这个数减去1e-29,依旧是个很小的数,相当之离奇,搁置不管了

但至此我发现:

判断最短路是顶点加入

判断是否有负/正环,是所有点都加入,因为每个边有权值

判断汇率问题,虽是判断正环,但由于本金在初始点手里,其他点进来哪怕有汇率权值,也由于没有本金(那本金即为0乘任何汇率没意义),没意义,所以只有顶点加入即可,最清爽最反应算法和题目本质的AC代码

 1 #include<stdio.h>
 2 #include<vector>
 3 #include<queue>
 4 #include<string.h>
 5 #include<iostream>
 6 using namespace std;//vector必须要有这句
 7 struct Edge{
 8     int to;
 9     double rate;
10     double commission;
11 } edge;
12     int cnt[101];
13 vector<Edge>G[101];//最多100种货币
14 queue<int>q;
15 double D[101];
16 int flag;
17 void SPFA();
18 int N,M,S;
19 double V;//money
20 int a,b;
21 int main()
22 {
23 //    freopen("zhishu.txt","r",stdin);
24     double rate_ab, commission_ab;
25     double rate_ba, commissiom_ba;
26     while(cin>>N>>M>>S>>V){
27         while(!q.empty())
28             q.pop();
29         flag=0;
30         memset(D,0,sizeof(D));
31         memset(cnt,0,sizeof(cnt));
32         for(int i=0;i<M;i++){
33             cin>>a>>b>>rate_ab>>commission_ab>>rate_ba>>commissiom_ba;
34             edge.to=b;
35             edge.rate=rate_ab;
36             edge.commission=commission_ab;
37 //            edge.cnt=0;
38             G[a].push_back(edge);
39 //            q.push(a);
40 
41             edge.to=a;
42             edge.rate=rate_ba;
43             edge.commission=commissiom_ba;
44 //            edge.cnt=0;
45             G[b].push_back(edge);
46 //            q.push(b);
47         }//至此存图结束
48 
49         SPFA();
50 
51         if(flag==1)
52             cout<<"YES"<<endl;
53         else
54             cout<<"NO"<<endl;
55 
56     }
57 }
58 void SPFA()
59 {
60     D[S]=V;
61     q.push(S);
62     while(!q.empty()){
63         int u;
64         u=q.front();
65 //        cout<<u<<endl;
66         q.pop();
67         for(int i=0;i<G[u].size();i++){
68             int v=G[u][i].to;
69             double rate=G[u][i].rate;
70             double commission=G[u][i].commission;
71 //cout<<(D[u]-commission)*rate<<" "<<D[v]<<endl;
72             if((D[u]-commission)*rate>D[v]){
73                 D[v]=(D[u]-commission)*rate;
74 //                cout<<D[v]<<endl;
75                 cnt[v]++;
76                 if(cnt[v]>N-1){
77                     flag=1;
78                     return;
79                 }
80                 q.push(v);
81             }
82         }
83     }
84 }
View Code

对于那个题,无论是SPFA的只有顶点加入队列还是都加入队列(其他点没本金,无意义),亦或是贝尔曼,都可以解决出发点和环不连接的情况,如下例子

4 3 1 1.0
2 3 2 0 2 0
3 4 2 0 2 0
4 2 2 0 2 0
View Code

但又发现并没有练到n个点都加入队列的情况,上面n个点都加入队列因为本身用不到,如果去写虫洞那个题,会WA。

-------------------------------------------至此说的都是前面Currency Exchange那题的事--------------------------------------------

 

这道题才是货真价实练习n个点进入队列SPFA的,即判断正环

差别是现在这题并没说初始货币编号,必须都加入队列

Currency Exchange那个题说了初始编号,所以只需初始一个点加入队列就行,但如果都入队列是不行的,虽然是可以,但要知道可以的理由是只有真正顶点有本金,其他点是0,入队也没意义,而如果真判断从指定顶点出发有无正环,比如这个数据,其他点如果也有本金,但我只要S号的,那就会错误,因为我要的S号没法自增套利,但却有自增环 —— 妈的都快写吐了,头晕目眩

4 3 1 1.0
2 3 2 0 2 0
3 4 2 0 2 0
4 2 2 0 2 0
View Code

虫洞那个题是真正练习负环的(跟这个题一样,只加入初始一个点不行

 

开始写这个题,不打算在原代码改动,依旧重写,打开虫洞那题的博客里的洛谷ljcljc、wjy666两位选手抄vector格式

题目没给范围

题目没说再回到美元(虽然肯定要回的,但没说第一行就是初试持有的货币,n个点都加入?)

题目没说初始本金

麻痹的这么不严谨吗???没那个题基础根本想不到是判断自增环

那我懂了,假设每个点初始值1(即本金),多源(即遍历n个点作为起点,然后每次都把n个点都加入队列,只要有自增正环即是答案)。

看似是n个点都压入队列,仿佛不用遍历n个起点多源,但本质是起点到某点的距离,加路径权值,跟起点到线段终点做比较,起点不同,情况有所不同

经过仔细研究发现,虫洞博客里洛谷ljcljc选手的SPFA进入n次代码真的经典,太牛逼了,这个写法又有很多巧妙的地方,虫洞博客里我和AC博客那人都是贝尔曼不涉及进入队列n次的问题,忘掉贝尔曼和SPFA的东西(束缚)来思考就会顺很多

 

首先如果是Currency Exchange博客里,“最完美解答” 那个博客说的,只有一个点加入不可以判断环,所以必须n个点加入队列, 

松弛更新一遍后,这个点vis变为0,意思是下一次弹出的点,如果可以把这个点更新就更新,再压入队列,而那些vis本来就是1的,如果可以松弛将他们更新,反正下次也在队里不用在压入。

但想个事,vis有必要么?每个地方想懂好吧?别跟个der一样背模板,我把vis去掉发现POJ和洛谷均可AC!!针对ljcljc选手的代码(他用的边数做边界条件)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

const int N = 510;
const int M = 5500;
int n, m1, m2;
int cnt[N], vis[N], dis[N];
struct edge {
    int to, w;
};
vector<edge> G[M];

bool spfa() {
    queue<int> q;
    memset(cnt, 0, sizeof cnt);
    memset(vis, 0, sizeof vis);
    memset(dis, 0, sizeof dis);
    for(int i=1; i<=n; i++) {
        q.push(i);
        vis[i] = 1;
    }
    while(!q.empty()) {
        int u = q.front();
        q.pop();
//        vis[u] = 0;
        for(int i=0; i<G[u].size(); i++) {
            int v = G[u][i].to;
            int w = G[u][i].w;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                cnt[v] = cnt[u] + 1;
                if(cnt[v] >= n) return true;
//                if(!vis[v]) {
                    q.push(v);
//                    vis[v] = 1;
//                }
            }
        }
    }
    return false;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        memset(G, 0, sizeof(G));
        scanf("%d %d %d", &n, &m1, &m2);
        while(m1--) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back((edge){v, w});
            G[v].push_back((edge){u, w});
        }
        while(m2--) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back((edge){v, -w});
        }
        if(spfa()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
View Code

 

这个题我用点数做边界条件,一遍SPFA,这一遍里每个点都在最开始加入队列(当顶点),本金都初始为1,有正环就YES

很巧妙的一点就是,之前以为每个点都要当起点遍历,然后再每个点都加进来, 仿佛不用遍历n个起点多源,但本质是起点到某点的距离,加路径权值,跟起点到线段终点做比较,起点不同,情况有所不同 ,但其实每个点都memset为1就相当于自身到自身了相当巧妙,如果取出这个顶点时候本金不是1了咋办?无所谓,只要是正环,就会再变大

设为1的玄妙之处,或者觉得,如果我第一步本金1乘了个很小的汇率,你没加进来,但后面其实有正环呢?有这疑问的见这个虫洞博客,去里面ctrl+F找关键词“初始fl是0”

玄妙点:

如果我没指定初始货币编号,所有点都设置本金为1,很巧妙的相当于每个点都当作顶点,如果指定顶点到某点导致小于1,那哪怕该顶点有参与形成后面自增环,那也可舍弃,因为这个是帮倒忙,有你参与有正环那我只需要去找真正形成正环的那个大汇率即可

如果指定了初始货币编号,指定的点为1,其他点都为0,这样,只要增大的点就都会进来,哪怕很小的汇率也是大于0的,而如果顶点和自增环不通,那就还是0

 

 1 #include<stdio.h>
 2 #include <iostream>
 3 #include<queue>
 4 #include<string.h>
 5 #include <vector>
 6 #include <algorithm>//cnm没这个find函数提示报错
 7 using namespace std;
 8 int N;
 9 struct Edge{
10     int to;
11     double rate;
12 }edge;
13 vector<Edge>G[8000];//cnm范围也没有!假设100个单向兑换的汇率情况,即100条边
14 vector<string>get_position;
15 queue<int>q;
16 void SPFA();
17 double D[510];
18 int cnt[510];
19 int flag;
20 int main()
21 {
22 //    freopen("zhishu.txt","r",stdin);
23     int Case=1;
24     while(cin>>N&&N){
25         memset(G,0,sizeof(G));
26         for(int i=1;i<=N;i++){
27             string str;
28             cin>>str;
29             get_position.push_back(str);
30         }
31         int M;
32         cin>>M;
33         for(int i=0;i<M;i++){
34             string str_a;
35             double rate;
36             string str_b;
37             cin>>str_a>>rate>>str_b;
38 
39             auto it = find(get_position.begin(),get_position.end(),str_a);
40             int position_a=distance(get_position.begin(),it)+1;//从0开始的
41             /*auto*/it = find(get_position.begin(),get_position.end(),str_b);
42             int position_b=distance(get_position.begin(),it)+1;//从0开始的
43 
44             edge.to=position_b;
45             edge.rate=rate;
46             G[position_a].push_back(edge);
47 //            cout<<"@"<<str_a<<" "<<edge.to<<endl;
48             q.push(position_a);
49         }
50 
51 //        memset(D,1,sizeof(D));//草你奶奶,傻逼memset
52         for(int i=1;i<=N;i++)
53             D[i]=1;
54 //        cout<<D[2]<<endl;
55         memset(cnt,0,sizeof(cnt));
56         flag=0;
57         SPFA();
58         if(flag==1)
59             cout<<"Case "<<Case<<": "<<"Yes"<<endl;
60         else
61             cout<<"Case "<<Case<<": "<<"No"<<endl;
62         Case++;
63     }
64 }
65 void SPFA()
66 {
67     while(!q.empty()){
68         int u;
69         u=q.front();
70         q.pop();
71         for(int i=0;i<G[u].size();i++){
72             int v=G[u][i].to;//这句模板总忘记不知道咋写,其实记住是二位数组就好
73             double rate=G[u][i].rate;
74 //            cout<<"^"<<D[u]<<" "<<rate<<" "<<D[u]*rate<<endl;
75 //            cout<<rate<<endl;
76             if(D[v]<D[u]*rate){
77                 D[v]=D[u]*rate;
78                 cnt[v]++;
79 //                cout<<u<<" "<<cnt[v]<<" "<<D[v]<<endl;//惊了,为啥会出现e-303这个数量级的数
80                 if(cnt[v]==N){
81                     flag=1;
82                     while(!q.empty())
83                         q.pop();
84                     return;
85                 }
86                 q.push(v);
87             }
88         }
89     }
90 }
View Code

代码如上

范围没给,洛谷RE,逐次增大范围提交,最后

洛谷/Hdu/可用平台:WA

POJ:CE

Main.cc: In function 'int main()':

Main.cc:39: error: ISO C++ forbids declaration of 'it' with no type

Main.cc:39: error: cannot convert '__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >' to 'int' in initialization

Main.cc:40: error: no matching function for call to 'distance(__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, int&)'

Main.cc:41: error: cannot convert '__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >' to 'int' in assignment

Main.cc:42: error: no matching function for call to 'distance(__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, int&)'
View Code

真tm要疯了

 

后来发现用不到边数

但为啥会RE呢

 

对拍代码

 1 #include <iostream>
 2 #include <stdlib.h>
 3 #include <time.h>
 4 #include<stdio.h>
 5 #include<iostream>
 6 #include<time.h>
 7 #include <string>
 8 #include <random>
 9 using namespace std;
10 
11 string randomStrGen(int length) {
12     static string charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
13     string result;
14     result.resize(length);
15 
16 //    srand(time(NULL));
17     srand(time(0) + (unsigned long long)(new char));
18     for (int i = 0; i < length; i++)
19         result[i] = charset[rand() % charset.length()];
20 
21     return result;
22 }
23 
24 random_device rd;
25 mt19937 gen(rd());
26 uniform_real_distribution<> dis(0.0, 10.0);
27 
28 string coin[32];
29 int n;
30 int a;
31 int b;
32 string str;
33 int main(){
34     srand(time(0) + (unsigned long long)(new char));
35     int n = rand()%30+1;
36     cout<<n<<endl;
37     for(int i=1;i<=n;i++){
38 
39         str=randomStrGen(4);
40         cout << str<<endl;
41         coin[i]=str;
42 
43     }
44 
45     int m = rand()%70+1;
46     cout<<m<<endl;
47     for(int i=0;i<m;i++){
48         int a = rand()%n+1;
49         double random_float = dis(gen); // 生成一个[0.0, 10.0)区间内的浮点数
50         int b = rand()%n+1;
51         cout<<coin[a]<<" "<<random_float<<" "<<coin[b]<<endl;
52     }
53 }
View Code

 

但用洛谷题解做对拍,为什么输入这组数据没反应???那对拍程序咋过的这组数据??? 对拍一直没发现问题啊。后来发现是输入0才能有输出

测试数据:

17
hl4H
YKv6
EDqZ
l3Sn
2VNh
IkfF
XaFO
Ez7I
ks37
2Rt1
HKVo
o0Qi
53iG
LrdA
rkFY
ZbXk
F5SI
54
rkFY 7.26249 XaFO
LrdA 9.15339 l3Sn
l3Sn 5.94324 EDqZ
l3Sn 5.15358 Ez7I
l3Sn 9.75149 Ez7I
EDqZ 6.61561 l3Sn
ks37 5.28652 2Rt1
XaFO 7.88493 hl4H
l3Sn 0.741007 EDqZ
2Rt1 3.2985 LrdA
o0Qi 5.83744 o0Qi
l3Sn 0.309722 rkFY
rkFY 9.28593 ZbXk
o0Qi 0.197524 ks37
EDqZ 7.79372 EDqZ
HKVo 3.36045 XaFO
o0Qi 3.42526 YKv6
ks37 4.1482 IkfF
F5SI 9.07313 2VNh
Ez7I 6.5148 LrdA
HKVo 5.7063 rkFY
LrdA 0.0782814 Ez7I
2Rt1 2.86952 YKv6
Ez7I 7.29566 F5SI
o0Qi 7.024 XaFO
rkFY 4.52956 ks37
hl4H 2.23708 hl4H
2Rt1 8.72067 YKv6
XaFO 7.1745 o0Qi
l3Sn 8.85457 ks37
hl4H 3.36358 EDqZ
IkfF 6.71428 LrdA
o0Qi 6.05524 IkfF
ks37 0.666561 hl4H
YKv6 7.68242 LrdA
o0Qi 8.26867 XaFO
LrdA 1.44854 2Rt1
hl4H 0.0238443 YKv6
IkfF 4.93874 rkFY
rkFY 0.970056 HKVo
2Rt1 6.92356 2VNh
F5SI 6.25061 53iG
hl4H 7.54976 F5SI
Ez7I 4.61797 ZbXk
2Rt1 7.77466 53iG
EDqZ 7.51587 YKv6
HKVo 9.76268 rkFY
rkFY 1.7796 EDqZ
ks37 4.49552 LrdA
2VNh 8.35422 Ez7I
Ez7I 5.76174 o0Qi
IkfF 3.99789 ZbXk
HKVo 7.91795 hl4H
HKVo 4.9201 o0Qi



3
xofA
ehHu
UGCS
45
UGCS 7.26249 UGCS
xofA 9.15339 UGCS
xofA 5.94324 UGCS
UGCS 5.15358 UGCS
ehHu 9.75149 xofA
xofA 6.61561 xofA
ehHu 5.28652 UGCS
UGCS 7.88493 xofA
xofA 0.741007 xofA
xofA 3.2985 UGCS
ehHu 5.83744 xofA
ehHu 0.309722 ehHu
xofA 9.28593 UGCS
UGCS 0.197524 ehHu
ehHu 7.79372 UGCS
xofA 3.36045 ehHu
ehHu 3.42526 ehHu
xofA 4.1482 xofA
xofA 9.07313 UGCS
ehHu 6.5148 ehHu
xofA 5.7063 UGCS
ehHu 0.0782814 ehHu
UGCS 2.86952 xofA
ehHu 7.29566 xofA
xofA 7.024 UGCS
xofA 4.52956 ehHu
UGCS 2.23708 UGCS
ehHu 8.72067 ehHu
xofA 7.1745 xofA
UGCS 8.85457 xofA
ehHu 3.36358 ehHu
ehHu 6.71428 xofA
ehHu 6.05524 ehHu
xofA 0.666561 UGCS
xofA 7.68242 xofA
xofA 8.26867 UGCS
xofA 1.44854 UGCS
xofA 0.0238443 xofA
xofA 4.93874 xofA
ehHu 0.970056 xofA
ehHu 6.92356 UGCS
xofA 6.25061 ehHu
UGCS 7.54976 xofA
ehHu 4.61797 UGCS
UGCS 7.77466 ehHu





1
DxQY
1
DxQY 7.26249 DxQY
View Code

且他输出好诡异,统一输出我的天

  

操你血奶奶,加了句  get_position.clear(); 就过了

常规操作是初始化清空,但我认为没必要啊,比如题目测试样例,第一组数据,那三种货币是123,在get_position容器中,U是1号,B是2号,F是3号。第二组数据也是这仨,但压入编号往下递增,U是4号,B是5号,F是6号,在下面找的时候,只会找第一次出现的,所以现在第二组数据数据完,UBF压入到了456的位子,但找的时候,position_a和position_b,返回的是UBF为123,可我觉得没问题啊,由于队列清空了,找UBF赋值汇率(权值)的时候,那就是123之间没啥毛病啊

将上面的测试数据精简为:

3
hl4H
YKv6
EDqZ
0

3
xofA
ehHu
UGCS
1
UGCS 7.26249 UGCS
View Code

发现输出No和No

而单独用第二个测试数据输出Yes

经过研究这个精简数据懂了,理论上我之前的想法确实没问题,但问题在D[i]初始化那,之前RE由于D只开了31个,可以容纳1~30的几个点,但我这么一搞如果第一次输入30个点,下次就会从31往下输入,如果下次输入10种货币,N是10,就会从31到40(至此RE也就理解了),后面 D[v]<D[u]*rate 判断的时候,我只把D[1]~D[N]即D[1]~D[10]用memset为1了,但用到的其实是D[31]~D[40](WA也就理解了)

 

AC代码 —— 注释值得一看

 1 #include<stdio.h>
 2 #include <iostream>
 3 #include<queue>
 4 #include<string.h>
 5 #include <vector>
 6 #include <algorithm>//cnm没这个find函数提示报错
 7 using namespace std;
 8 int N;
 9 struct Edge{
10     int to;
11     double rate;
12 }edge;
13 vector<Edge>G[31];//cnm范围也没有!假设100个单向兑换的汇率情况,即100条边,后来发现最大就30,跟边数无关
14 vector<string>get_position;
15 queue<int>q;
16 void SPFA();
17 double D[31];
18 int cnt[31];
19 int flag;
20 int main()
21 {
22 //    freopen("zhishu.txt","r",stdin);
23     int Case=1;
24     while(cin>>N&&N){
25         get_position.clear();
26         memset(G,0,sizeof(G));//ljcljc 用memset0了,但不可以吧?,get_position都不行
27         //get_position没必要吧?应该memset
28         for(int i=1;i<=N;i++){
29             string str;
30             cin>>str;
31             get_position.push_back(str);
32 //            cout<<"!"<<get_position.size()<<endl;
33         }
34         int M;
35         cin>>M;
36         for(int i=0;i<M;i++){
37             string str_a;
38             double rate;
39             string str_b;
40             cin>>str_a>>rate>>str_b;
41 
42             auto it = find(get_position.begin(),get_position.end(),str_a);
43             int position_a=distance(get_position.begin(),it)+1;//从0开始的
44             /*auto*/it = find(get_position.begin(),get_position.end(),str_b);
45             int position_b=distance(get_position.begin(),it)+1;//从0开始的
46             edge.to=position_b;
47 //cout<<"#"<<position_a<<" "<<position_b<<endl;
48             edge.rate=rate;
49             G[position_a].push_back(edge);
50 //            cout<<"@"<<str_a<<" "<<edge.to<<endl;
51             q.push(position_a);
52         }
53 
54 //        memset(D,1,sizeof(D));//草你奶奶,傻逼memset
55         for(int i=1;i<=N;i++)
56             D[i]=1;
57         memset(cnt,0,sizeof(cnt));
58         flag=0;
59         SPFA();
60         if(flag==1)
61             cout<<"Case "<<Case<<": "<<"Yes"<<endl;
62         else
63             cout<<"Case "<<Case<<": "<<"No"<<endl;
64         Case++;
65     }
66 }
67 void SPFA()
68 {
69     while(!q.empty()){
70         int u;
71         u=q.front();
72 //        cout<<"*"<<u<<endl;
73         q.pop();
74         for(int i=0;i<G[u].size();i++){
75             int v=G[u][i].to;//这句模板总忘记不知道咋写,其实记住是二位数组就好
76             double rate=G[u][i].rate;
77 //            cout<<"^"<<D[u]<<" "<<rate<<" "<<D[u]*rate<<endl;
78 //            cout<<rate<<endl;
79             if(D[v]<D[u]*rate){
80                 D[v]=D[u]*rate;
81                 cnt[v]++;
82 //                cout<<u<<" "<<cnt[v]<<" "<<D[v]<<endl;//惊了,为啥会出现e-303这个数量级的数
83                 if(cnt[v]==N){
84                     flag=1;
85                     while(!q.empty())
86                         q.pop();
87                     return;
88                 }
89                 q.push(v);
90             }
91         }
92     }
93 }
View Code

但POJ好像不支持C++11,报错CE

更改后全部平台均可AC!

AC代码

 1 #include<stdio.h>
 2 #include <iostream>
 3 #include<queue>
 4 #include<string.h>
 5 #include <vector>
 6 #include <algorithm>
 7 using namespace std;
 8 int N;
 9 struct Edge{
10     int to;
11     double rate;
12 }edge;
13 vector<Edge>G[31];
14 vector<string>get_position;
15 queue<int>q;
16 void SPFA();
17 double D[31];
18 int cnt[31];
19 int flag;
20 int main()
21 {
22     int Case=1;
23     while(cin>>N&&N){
24         get_position.clear();
25         memset(G,0,sizeof(G));//ljcljc 用memset0了,但不可以吧?,get_position都不行
26         for(int i=1;i<=N;i++){
27             string str;
28             cin>>str;
29             get_position.push_back(str);
30         }
31         int M;
32         cin>>M;
33         for(int i=0;i<M;i++){
34             string str_a;
35             double rate;
36             string str_b;
37             cin>>str_a>>rate>>str_b;
38 
39             
40             int position_a=distance(get_position.begin(),find(get_position.begin(),get_position.end(),str_a))+1;
41             int position_b=distance(get_position.begin(),find(get_position.begin(),get_position.end(),str_b))+1;
42             edge.to=position_b;
43             edge.rate=rate;
44             G[position_a].push_back(edge);
45             q.push(position_a);
46         }
47 
48         for(int i=1;i<=N;i++)
49             D[i]=1;
50         memset(cnt,0,sizeof(cnt));
51         flag=0;
52         SPFA();
53         if(flag==1)
54             cout<<"Case "<<Case<<": "<<"Yes"<<endl;
55         else
56             cout<<"Case "<<Case<<": "<<"No"<<endl;
57         Case++;
58     }
59 }
60 void SPFA()
61 {
62     while(!q.empty()){
63         int u;
64         u=q.front();
65         q.pop();
66         for(int i=0;i<G[u].size();i++){
67             int v=G[u][i].to;
68             double rate=G[u][i].rate;
69             if(D[v]<D[u]*rate){
70                 D[v]=D[u]*rate;
71                 cnt[v]++;
72                 if(cnt[v]==N){
73                     flag=1;
74                     while(!q.empty())
75                         q.pop();
76                     return;
77                 }
78                 q.push(v);
79             }
80         }
81     }
82 }

容器不能memset,用clear() ;

但那个G[31]的容器,clear();报错,估计是表示将二维数组清空赋0,和容器无关?麻痹的全网找不到靠谱的解答。先搁置!!

 

 

 AC后看看别人咋写的,研究下上面那个洛谷题解:(vis依旧没用,去掉可以AC)

1、起初我发现string没法做下标,学到了可以用map,即 mp[s]=i; s为string类型。输入的时候兑换汇率对是从1到N,那0就这个二维容器就存储

2、了解了冒号for,他这个代码好JB高深,但可读性不好,看似只把顶点加入队列,其实冒号for里跟迪杰斯特拉一样,省去了初始化赋值1的操作,在冒号for里执行初始化1的操作

3、他这个main里写法挺巧妙的,改成

while(cin>>n&&n!=0)
        for(int i=1;i<=n;i++)
            cout<<"Case "<<i<<": ",solve();
}
View Code

不对

4、关于他的  ios::sync_with_stdio(false); cin.tie(0); 发现挺玄妙的,居然是输入0使得程序结束后才输出所有,那对拍程序咋过的,参考博客。不说是打消输入输出缓存吗?但怎么

仿佛自建了个答案队列,好tm离奇,先搁置

 

至此此题结束

 

 

###:int的范围是到2147483647,超过了可以cout是没有问题的,但如果是用超了的值给int变量赋值则会溢出,是有问题的

    int a=2147483647;
    int b=a+2147483677;
    cout<<b<<endl;
    cout<<a+2147483677<<endl;
View Code

###:查找vector中指定元素位置

###:洛谷SPOJ一直返回UKE,查解决办法,说要下插件,懒得打开github,之前装Chrome插件慢得要死,直接开个tizi验证码就出来了,注册填的东西有点小麻烦。

之前chrome的翻译插件就好久都不能用了,现在发现开了tiziChrome插件的翻译依旧不好使,好像挂了

###:回忆spoj和codeingame,北大文科矩阵67大佬

###:洛谷数据生成

###;发现为啥一堆傻逼玩意不写using namespace std;反而每句话前面去写一堆std::,纯纯傻逼

###:写对拍的时候实在找不到可以随机生成不同字符串的博客,草泥马这群傻逼,写个随机都不会写

最后我把 srand(time(NULL)); 换成最早对拍博客里的 srand(time(0) + (unsigned long long)(new char)); 就好了(不然虽然每次运行随机生成的字符串不一样,但是一次运行我需要好多个随机字符串啊,一次运行的时候产生的字符串却都是相同的)

C++随机生成浮点数是百度智能AI回答的

###:double用memset为1 坑我好一会

###:

电脑刷题就没关过机,经常用,再也没3F0放电和Chrome闪退过

不知道为啥Adblock过滤列表没了艹!!必应设置(页面关闭广告新闻那些)也没了,刚才3F0了,每次放电都要丢这些存储块么??

想想那个BUPT260多分进去复试第一的标题党真牛逼,本科项目做的很多,几天就能突击现学上机,把机试AK真牛(突击掌握知识是我永远的软肋弱点),正常轨迹我也是吧。他们都可以架空空中楼阁没有前设知识去理解去学东西。连控制台怎么复制粘贴都不知道(只能右键,没法Ctrl+CV),19BUPT纸盒电梯现学队列

对面戴口罩女的眼睫毛好好看又tm分心无法专注
View Code

###:

posted @ 2024-10-30 15:48  GerJCS  阅读(5)  评论(0编辑  收藏  举报