1031~1040
1030[Dijksra+DFS]
思路:
又是喜闻乐见的图论,呵呵。和前面自行车问题一样(MD记住你了),先Dijkstra求出最短路径,在此过程中求出每个结点的前驱结点。然后DFS,从终点回到起点(DFS的边界就是到达起点了,这时候由起点到终点的唯一路径已经确定,计算费用即可),看是否是最低消费。如果是就更新全局变量~~~
这里的DFS不需要visited数组了,因为由于实际意义的限制,DFS方向一直会向前的,不会重新遍历已经遍历过的结点!!!
//
// @author prime on 2017/6/15.
//
#include <iostream>
#include <vector>
using namespace std;
const int inf=99999999;
int G[510][510];
int cost[510][510];
int dist[510];
bool visited[510];
int N,M,S,D;
vector<int> pre[510];
vector<int> path,tmp_path;
int min_cost=inf,tmp_cost;
void DFS(int u);
int main()
{
/*初始化各种变量*/
fill(visited,visited+510, false);
fill(G[0],G[0]+510*510,inf);
fill(dist,dist+510,inf);
scanf("%d%d%d%d",&N,&M,&S,&D);
for (int i=0,u,v;i<M;++i)
{
scanf("%d %d",&u,&v);
scanf("%d %d",&G[u][v],&cost[u][v]);
G[v][u]=G[u][v];
cost[v][u]=cost[u][v];
}
dist[S]=0;
//pre[S].push_back(S);
for (int i=0;i<N;++i)
{
int min_dist=inf,u=-1;
for (int j=0;j<N;++j)
{
if(!visited[j]&&min_dist>dist[j])
{
min_dist=dist[j];
u=j;
}
}
if(u==-1)
break;
visited[u]=true;
for (int v=0;v<N;++v)
{
if(!visited[v]&&G[u][v]!=inf)
{
if(dist[v]>dist[u]+G[u][v])
{
pre[v].clear();
pre[v].push_back(u);
dist[v]=dist[u]+G[u][v];
} else if(dist[v]==dist[u]+G[u][v])
{
pre[v].push_back(u);
}
}
}
}
//fill(visited,visited+N, false);
DFS(D);
for (int i=path.size()-1;i>=0;--i)
printf("%d ",path[i]);
printf("%d %d",dist[D],min_cost);
return 0;
}
void DFS(int u)
{
//visited[u]=true;
if(u==S)
{//从终点已经回到起点
tmp_path.push_back(u);
tmp_cost=0;
for (int i=tmp_path.size()-1;i>0;i--)
{//从源点到目的结点的路径
int index=tmp_path[i];
int next_index=tmp_path[i-1];
tmp_cost+=cost[index][next_index];
}
if(tmp_cost<min_cost)
{
min_cost=tmp_cost;
path=tmp_path;
}
tmp_path.pop_back();
}
else
{
tmp_path.push_back(u);
for (int i=0;i<pre[u].size();i++)
{
DFS(pre[u][i]);
}
tmp_path.pop_back();
}
}
1031[字符串处理]
思路:
仔细读题,确定n1,n2,n3之间关系即可,然后创建个二维数组保存输出,填写之即可。
这里n~1~=n~2~,且满足n~1~+n~2~+n~3~-2=N,并且n~2~在[3,N]之间,求n~1~和n~2~的最大值。
//
// @author prime on 2017/6/16.
//
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
string input;
cin>>input;
int N=input.size();
int n1=0,n3;
for (int n2=3;n2<=N;n2++)
{
if((N-n2+2)%2==0&&(N-n2+2)/2>n1&&(N-n2+2)/2<=n2)
n1=(N-n2+2)/2;
}
n3=n1;
int n2=N+2-n1-n3;
char out[n1][n2];
memset(out,' ', sizeof(out));
for (int i=0;i<n1;i++)
{
out[i][0]=input[i];
}
for (int i=0,j=n1-1;i<n2;++i,j++)
out[n1-1][i]=input[j];
for (int i=0,j=N-1;i<n3;++i,--j)
{
out[i][n2-1]=input[j];
}
for (int i=0;i<n1;++i)
{
for (int j=0;j<n2;j++)
cout<<out[i][j];
cout<<endl;
}
return 0;
}
1032[找链表公共结点]
思路:
首先沿着一条链遍历,把这条路径上的结点记录到hash表中,然后从另一条链遍历,找到在hash表中的结点就输出并退出。
这个题关键在于hash表怎么建立?用set?不行 !最后一个用例太大,用set这个基于红黑树的结构还是太慢,会超时!用unorder_set?想法不错,这是c++11引入的,但是支持似乎还不够完善。还是用数组吧,手动hash!
另外说明一下,set的比较规则,其模板参数中有比较器这一参数,默认less<T>
即调用T类型的<运算符进行比较。同理也有其它内置的仿函数。需要明确的是,set这个集合的等于定义为:假设a==b,则意味(!a
//
// @author prime on 2017/6/16.
//
#include <iostream>
using namespace std;
struct Node
{
char val;
int next;
bool flag;
Node()= default;
Node(char v,int n)
{
flag= false;
this->val=v;
this->next=n;
}
};
int main()
{
Node s[100000];
int N,start1,start2;
scanf("%d%d%d",&start1,&start2,&N);
for(int i=0;i<N;++i)
{
int addr,n;
char v;
scanf("%d %c %d",&addr,&v,&n);
s[addr].next=n;
s[addr].val=v;
}
for (int begin=start1;begin!=-1;begin=s[begin].next)
{
s[begin].flag= true;
}
for (int begin=start2;begin!=-1;begin=s[begin].next)
{
if(s[begin].flag)
{
printf("%05d",begin);
return 0;
}
}
printf("-1");
return 0;
}
1033[贪心]
思路:
基于贪心的准则,首先判断第一个加油站是不是距离为0,不满足说明根本走不了,直接退出。如果满足了,那么采取如下策略:即假设每次在加满油的情况下,看所能到达的所有加油站,如果有个比当前加油站便宜的,那就只维持油量到那个加油站;如果都比这个加油站贵,那就不用多说了,直接加满,能用便宜的谁也不想用贵的不是?还有一个注意点,为了方便结束,添加一个加油站代表终点,距离为目的距离,油价为0.
就是这么简单的思路,想全码对也不容易啊,还是缺少训练。
““c++
//
// @author prime on 2017/6/17.
//
include
include
include
using namespace std;
struct Station
{
double dis;//距离起点距离
double per;//单位燃料价格
};
bool cmp(const Station& o1,const Station &o2)
{
return o1.dis
1034[DFS+连通分量]
思路:
和求图的连通分量思路很像,但是有点区别。题目要求找一个集群,其中边权和大于阈值k且包含2个以上成员(老大也算),即连通分量必须保证结点大于2且边权和大于k。所以在DFS后要进行判断是否满足条件。而对于DFS过程,首先用visted数组记录已被遍历过的点,然后判断当前结点是不是老大,之后就是DFS的标准流程了,只不过,每次内部DFS之前,要把边权置0,为了保证total不被重复计算!
注意点:
- 输入的时候边输入权值边求点的权值
- 题目说N是电话记录,包括两个结点,可能极端到1000个电话记录都不有人重复,所以结点至少2000
- 人名是string,用map转化为int方便图的计算
//
// @author prime on 2017/6/17.
//
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
int G[2010][2010];
int weight[2010];
bool visited[2010];
map<string,int> si;//名字到编号的映射
map<int,string> is;//编号到名字的映射
map<string,int> res;//老大和小弟数的键值对
int number=1;//顶点的个数
void DFS(int u,int &member,int &head,int &total)
{
visited[u]=true;
member++;
if(weight[u]>weight[head])
head=u;
for (int v=1;v<=number;++v)
{
if(G[u][v]>0)
{
total+=G[u][v];
G[u][v]=G[v][u]=0;//计算之后把边权置0,防止重复计算total
if(!visited[v])
{
DFS(v,member,head,total);
}
}
}
}
int main()
{
int N,K;
scanf("%d%d",&N,&K);
for (int i=0;i<N;++i)
{
string name1,name2;int time;
cin>>name1>>name2>>time;
if(si[name1]==0)
{
si[name1]=number;
is[number]=name1;
number++;
}
if(si[name2]==0)
{
si[name2]=number;
is[number]=name2;
number++;
}
G[si[name1]][si[name2]]+=time;
G[si[name2]][si[name1]]+=time;
weight[si[name1]]+=time;
weight[si[name2]]+=time;
}
fill(visited,visited+2010,false);
for (int u=1;u<=number;++u)
{
if(!visited[u])
{
int head=u,total=0,member=0;
DFS(u,member,head,total);
if(member>2&&total>K)
res[is[head]]=member;
}
}
cout<<res.size();
for (auto &e:res)
{
cout<<"\n"<<e.first<<" "<<e.second;
}
return 0;
}
1035[简单字符串替换]
简单的替换,细心就ok~~~比如输出格式
//
// @author prime on 2017/6/18.
//
#include <iostream>
using namespace std;
bool transform(string& s)
{
bool flag= false;
auto it=s.begin();
while(it!=s.end())
{
if(*it=='O')
{
*it='o';
flag=true;
}
else if(*it=='1')
{
*it='@';
flag=true;
} else if(*it=='0')
{
*it='%';
flag=true;
} else if(*it=='l')
{
*it='L';
flag=true;
}
it++;
}
return flag;
}
int main()
{
int N;
cin>>N;
pair<string,string> s[N];
for (int i=0;i<N;++i)
{
cin>>s[i].first>>s[i].second;
}
int res=0;
bool modify[N];
fill(modify,modify+N, false);
for (int i=0;i<N;++i)
{
if(transform(s[i].second))
{
res++;
modify[i]=true;
}
}
if(res>0)
{
cout<<res;
for(int i=0;i<N;++i)
{
if(modify[i])
cout<<"\n"<<s[i].first<<" "<<s[i].second;
}
}
else
{
if(N==1)
cout<<"There is 1 account and no account is modified";
else
cout<<"There are "<<N<<" accounts and no account is modified";
}
return 0;
}
1036[排序/求极值]
思路:
简单求极值,通过排序可以方便解决。但是,这种单一求极值的问题,完全不必要全部保存,就为了考pat而言,直接保存需要的信息即可!
注意题目要求字符不超过10个字符,所以申请11个字符空间。不然过不去!
//
// @author prime on 2017/6/18.
//
#include <iostream>
#include <vector>
using namespace std;
struct Student
{
char name[11];//只能写10个字符
char gender;//性别
char id[11];
int grade;
};
bool cmp(const Student& o1,const Student& o2)
{
return o1.grade>o2.grade;
}
int main()
{
int N;
scanf("%d",&N);
vector<Student> F,M;
for (int i=0;i<N;++i)
{
Student tmp;
scanf("%s %c %s %d",tmp.name,&tmp.gender,tmp.id,&tmp.grade);
if(tmp.gender=='F')
F.push_back(tmp);
else
M.push_back(tmp);
}
sort(F.begin(),F.end(),cmp);
sort(M.begin(),M.end(),cmp);
if(F.size()>0&&M.size()>0)
{
printf("%s %s\n%s %s\n",F[0].name,F[0].id,M.back().name,M.back().id);
printf("%d",F[0].grade-M.back().grade);
} else if(F.size()>0)
{
printf("%s %s\n",F[0].name,F[0].id);
printf("Absent\n");
printf("NA");
} else if(M.size()>0)
{
printf("Absent\n");
printf("%s %s\n",M.back().name,M.back().id);
printf("NA");
}
return 0;
}
//
// @author prime on 2017/6/18.
//
#include <iostream>
using namespace std;
int main()
{
string male,female;
int m_score=101,f_score=-1;
int N;
scanf("%d",&N);
for (int i=0;i<N;++i)
{
string name,sex,id;
int score;
cin>>name>>sex>>id>>score;
if(sex=="F")
{
if(score>f_score)
{
f_score=score;
female=name+" "+id;
}
}
else
{
if(score<m_score)
{
m_score=score;
male=name+" "+id;
}
}
}
if(f_score!=-1&&m_score!=101)
{
cout<<female<<"\n"<<male<<"\n"<<f_score-m_score;
}
else if(f_score!=-1)
{
cout<<female<<"\n";
cout<<"Absent\n";
cout<<"NA";
}
else if(m_score!=101)
{
cout<<"Absent\n"<<male<<"\nNA";
}
return 0;
}
1037[贪心]
题意就是两个序列,其中任意两个数相乘,要求结果相加最大。
思路:
先从小到大排序,然后从后向前遍历两个序列,都是正数就相乘加到结果中,否则结束循环;然后,从前向后遍历两个序列,如果都是负数,相乘加到结果中,否则结束循环。
一开始我想着从后往前,如果相乘小于0,就把指针都递减,这样就只有一个用例没过去~~~这个思路是错误的,只适用于序列长度相等的情况。因为如果两个序列长度不等就错解。
//
// @author prime on 2017/6/18.
//
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int NC,NP;
scanf("%d",&NC);
vector<int> coupon(NC);
for (int i=0;i<NC;++i)
{
scanf("%d",&coupon[i]);
}
scanf("%d",&NP);
vector<int> product(NP);
for (int i=0;i<NP;++i)
scanf("%d",&product[i]);
sort(coupon.begin(),coupon.end());//升序
sort(product.begin(),product.end());
long res=0;
for(auto c_it=coupon.rbegin(),p_it=product.rbegin();c_it!=coupon.rend()&&p_it!=product.rend()&&*c_it>0&&*p_it>0;c_it++,p_it++)
{
res+=(*c_it)*(*p_it);
}
for(auto c_it=coupon.begin(),p_it=product.begin();c_it!=coupon.end()&&p_it!=product.end()&&*c_it<0&&*p_it<0;c_it++,p_it++)
{
res+=(*c_it)*(*p_it);
}
printf("%ld",res);
return 0;
}
1038[字符串+贪心]
很经典的题目,要求给出几个数,求它们的最小的一种排列。
思路:
很自然联想到排序,那么是按照字典序排一下就ok了吗?显然不是!任何两个字符串a、b排列的依据应该是把a+b和b+a中较小的那一个放在前面,另一个放在后面,这样局部最优从而全局最优。
//
// @author prime on 2017/6/22.
//
#include <iostream>
#include <algorithm>
using namespace std;
bool cmp(const string &o1, const string &o2)
{
return o1+o2<o2+o1;
}
int main()
{
int N;
cin>>N;
string s[N];
for (int i=0;i<N;++i)
cin>>s[i];
sort(s,s+N,cmp);
string res;
for (int i=0;i<N;++i)
res+=s[i];
while(res.size()!=0&&res[0]=='0')//防止第一个元素是0
{
res.erase(res.begin());
}
if(res.size()==0)
cout<<'0';
else
cout<<res;
return 0;
}
1039[查找]
思路:
和前面中的1022思路一样,都是针对输出建立映射关系,这里要求查询学生选课,那么键就是学生,值就是选的课。由此,正常思路如下
//
// @author prime on 2017/6/22.
//
#include <iostream>
//#include <map>
#include <set>
#include <unordered_map>
using namespace std;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int N,K;
cin>>N>>K;
unordered_map<string,set<int>> course;
for (int i=0;i<K;++i)
{
int index,n;
string name;
cin>>index>>n;
for (int j=0;j<n;++j)
{
cin>>name;
course[name].insert(index);
}
}
string query;
for (int i=0;i<N;++i)
{
cin>>query;
cout<<query<<" "<<course[query].size();
for (auto &e:course[query])
cout<<" "<<e;
cout<<"\n";
}
return 0;
}
很遗憾,最后一个用例是过不去的。因为超时了。为此,有了下面这种手动hash的方法,虽然能百分百通过,不过考试时候还是谨慎点,别捡了芝麻丢了西瓜。
//
// @author prime on 2017/6/22.
//
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int my_hash(char *name)
{
int code=0;
for (int i=0;i<3;++i)
{
code=code*26+name[i]-'A';
}
code=code*10+name[3]-'0';
return code;
}
const int max_num=26*26*26*10+10;
vector<int> s[max_num];
int main()
{
int N,K;
scanf("%d%d",&N,&K);
char name[5];//永远记住多留一个存储\0,虽然本地可能正确,oj不这么认为!!!
for (int i=0;i<K;++i)
{
int index,n;
scanf("%d%d",&index,&n);
for (int j=0;j<n;++j)
{
scanf("%s",name);
s[my_hash(name)].push_back(index);
}
}
for (int i=0;i<N;++i)
{
scanf("%s",name);
int id=my_hash(name);//在hash表中的位置
sort(s[id].begin(),s[id].end());
printf("%s %lld",name,s[id].size());
for (auto & e:s[id])
printf(" %d",e);
printf("\n");
}
return 0;
}
1040[DP]
这道题也是经典题目,求最长回文子串的长度。
思路:
能用现有的算法,千万不要自己造轮子,容易出错!这道题可以用已有的求最长公共子串的DP算法,即把该串翻转后求和原串的最长公共子串的长度。
//
// @author prime on 2017/6/23.
//
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string in;
getline(cin,in);
size_t len=in.size();
int dp[len+1][len+1];
fill(dp[0],dp[0]+(len+1)*(len+1), 0);
string rev=in;
reverse(rev.begin(),rev.end());
int res=-1;
for (int i=1;i<=len;++i)
{
for (int j=1;j<=len;++j)
{
if(in[j-1]==rev[i-1])
{
dp[i][j]=dp[i-1][j-1]+1;
if(dp[i][j]>res)
{
res=dp[i][j];
}
}
else
dp[i][j]=0;
}
}
cout<<res;
return 0;
}