要求:不可以用STL库,属实有一点点难受
目录
题目A 冬奥村网络布线方案 (40 分)
解题思路:
这个题目和之前写的文章 “JLU数据结构第五次实验课解题报告——考察“图”论”第三题供电完全一样。
很显然这道题是要寻找最小支撑树,这里我用了Prim(普里姆算法),我们必须先找到装路由器需要费用最少的房间,把它当作支点延伸到其他房间,去寻找“最小支撑树”,装一个路由器或许比连接网线还要省钱,所以我先把装路由器的费用当作最省钱的虚边, 在寻找轻边的的过程中,把边权值与邻接点按照路由器费用比较,哪个小保存哪个,因此呢,最后找到的最小支撑树可能存在虚边。
之前存储图的容器是vector,不过要注意的是不可以用STL库里面封装的容器。我用的是邻接表的方式存图,我刚开始建立图用的是链表,但是发现超出了时间限制(注释掉的部分为链表实现方式)。所以我换成了用一个数组模拟vector存储(具体看代码)。
#include<bits/stdc++.h>
using namespace std;
const int maxsize = 605;
int P[maxsize];
int lowcost[maxsize];
int visited[maxsize];
int INF = -100005;
typedef struct Side
{
int num;
int Neighber[1000];
int cost[1000];
}Side;
Side Head[605];
long long Prim(int n)
{
long long sum = 0;
Side *p;
int v,k,u;
for(int i=0;i<n;i++)
{
lowcost[i] = P[i];
visited[i] = 0;
}
int mincost=P[1];//找出最小的安装路由器消耗价值
int mark=1;
for(int i=1;i<n;i++){
if(P[i]<mincost)
{
mincost = P[i];
mark=i;
}
}
visited[mark] = 1;
for(int i = 0;i<Head[mark].num;i++)
{
k=Head[mark].Neighber[i];
if(lowcost[k]>Head[mark].cost[i])lowcost[k]=Head[mark].cost[i];
}
for(int j=0;j<n-1;j++)
{
int ldist=10000002; //确定轻边
for(int i=0;i<n;i++)
{
if(lowcost[i]<ldist&&visited[i]==0){
ldist=lowcost[i];
u=i;
}
}
visited[u]=1;
for(int i = 0;i<Head[u].num;i++)
{
int v=Head[u].Neighber[i];
if(Head[u].cost[i]<lowcost[v]&&visited[v]==0){
lowcost[v]=Head[u].cost[i];
}
}
}
for(int i=0;i<n;i++){
sum += lowcost[i];
}
return sum;
}
void Creat_Graph(int n, int e)
{
int i,j,k;
int v1,v2,value;
for(i=0;i<n;i++){
Head[i].num = 0;
}
for(k=0;k<e;k++){
scanf("%d %d %d",&v1,&v2,&value);
Head[v1].Neighber[Head[v1].num]=v2;
Head[v1].cost[Head[v1].num] = value;
Head[v1].num++;
Head[v2].Neighber[Head[v2].num]=v1;
Head[v2].cost[Head[v2].num] = value;
Head[v2].num++;
}
}
/*typedef struct Side{
int VerAdj;//邻接顶点的序号
int cost;//边的权值
struct Side* next;
}Side;
typedef struct Vertex{
int Name;
Side* sideHead;
}Vertex;
Vertex Head[maxsize];
void Creat_Graph(int n, int e)
{
int i,j,k;
int v1,v2,value;
Side *p,*r;
for(i=0;i<n;i++){
Head[i].Name=i;
Head[i].sideHead=NULL;
}
for(k=0;k<e;k++){
scanf("%d %d %d",&v1,&v2,&value);
p=new Side;
p->VerAdj=v2;
p->cost=value;
p->next=NULL;
Side *q = Head[v1].sideHead;
if(q==NULL) Head[v1].sideHead=p;
else{
while(q->next!=NULL){
q=q->next;
}
q->next=p;
}
r=new Side;
r->VerAdj=v1;
r->cost=value;
r->next=NULL;
Side* R=Head[v2].sideHead;
if(R==NULL) Head[v2].sideHead=r;
else{
while(R->next!=NULL)
R=R->next;
R->next=r;
}
}
}
long long Prim(int n)
{
long long sum = 0;
Side *p;
int v,k,u;
for(int i=0;i<n;i++)
{
lowcost[i] = P[i];
visited[i] = 0;
}
int mincost=P[1];//找出最小的安装路由器消耗价值
int mark=1;
for(int i=1;i<n;i++){
if(P[i]<mincost)
{
mincost = P[i];
mark=i;
}
}
visited[mark] = 1;
for(p = Head[mark].sideHead;p!=NULL;p=p->next)
{
k=p->VerAdj;
if(lowcost[k]>p->cost)lowcost[k]=p->cost;
}
for(int j=0;j<n-1;j++)
{
int ldist=10000002; //确定轻边
for(int i=0;i<n;i++)
{
if(lowcost[i]<ldist&&visited[i]==0){
ldist=lowcost[i];
u=i;
}
}
visited[u]=1;
for(p = Head[u].sideHead;p!=NULL;p=p->next)
{
int v=p->VerAdj;
if(p->cost<lowcost[v]&&visited[v]==0){
lowcost[v]=p->cost;
}
}
}
for(int i=0;i<n;i++){
sum += lowcost[i];
}
return sum;
}*/
int main()
{
long long n,e;
scanf("%d %d",&n,&e);
for(int i=0;i<n;i++){
scanf("%d",&P[i]);
Head[i].num = 0;
}
Creat_Graph(n,e);
long long sum = Prim(n);
printf("%lld\n",sum);
return 0;
}
题目B、C 冬奥会网站功能提升 (40 分+20分)
暴力算法可过
需优化算法
解题思路:
题目中需要注意的地方就是要:按频率从大到小以及字典序将所有单词排序,去掉重复单词,然后就可以检测,并且注意若字典中存在本身这个单词,不输出自身。 还要注意输出格式问题。
这里有暴力解法和字典树插入、查找算法。
&在暴力解法中,运算时间长,所以个别函数调用,能少则少,比如strlen(),strcmp()。我发现用string比用char数组更快,没必要的循环就直接break。排序用快速排序法可以提速,冒泡排序、插入排序等我没有通过。若没有STL库不允许用的限制,sort()可以更快。
&在有字典序插入、查找算法中,速度已经很优化了。我还发现若用string存单词,需要用cout输出,比起用char数组存,printf输出,这可相当的费时间,竟然有100ms之差!!。
##用print输出(优化算法):
##用cout输出(优化算法):
看到这个原因,可谓是气的想吐血,但也很惊喜发现这个问题。
好了,上代码:
- 暴力算法:
#include<bits/stdc++.h> using namespace std; typedef struct dot { int num; string letter; }dot; dot INM[10005]; void check_suggest(string checkLetter,int n,int k) { int size = checkLetter.length(); int flag = 0; int count=0; int jump=0; for(int i = n-1;i >0 ;i--) { for(int j=0;j<size;j++) { if(checkLetter[j]==INM[i].letter[j]&&size<INM[i].letter.size())continue; else { jump=1; break; } } if(count==k) { break; } else if(jump == 0 && count <k && INM[i].num>0) { count++; cout<<INM[i].letter<<endl; } jump=0; } if(count==0) { printf("no suggestion\n"); } return ; } void QSort(int L, int R) { if (L >= R)return; dot t = INM[L]; int left =L,right =R; while (left < right) { while (left < right && INM[right].num >= t.num) { right--; } if (left < right) INM[left++] = INM[right]; while (left < right && INM[left].num < t.num) { left++; } if (left < right) INM[right--] = INM[left]; } INM[right] = t; QSort(L, left - 1); QSort(left + 1,R); } //bool cmp(dot a, dot b) //{ // if(a.num == b.num) return a.letter>b.letter; // return a.num<b.num; // } void delete_same(int n) { for (int i = 0; i < n; i++) { //if(INM[i].num==-1)continue; for (int j = i + 1; j < n; j++) { if(INM[j].num==-1)continue; if (INM[i].letter==INM[j].letter) { if(INM[i].num>INM[j].num) { INM[j].num=INM[i].num; } INM[i].num = -1; } } } } void dictionary_palindromic(int n) { for (int i = n-1; i >=0; i--) { for (int j = i - 1; j >= 0; j--) { if (INM[i].num!=INM[j].num) { break; } else if(INM[i].letter>INM[j].letter) { swap(INM[i],INM[j]); } } if(INM[i].num==-1) { break; } } } /* 快速排序从大到小,比较慢 int partition(int left, int right) { int pivot = INM[right].num; int i = left - 1; for (int j = left; j < right; j++) { if (INM[j].num > pivot) { i++; swap(INM[i], INM[j]); } } swap(INM[i+1], INM[right]); return i + 1; } void quickSort(int left, int right) { if (left < right) { int mid = partition(left, right); quickSort(left, mid - 1); quickSort(mid + 1, right); } }*/ int main() { int n,m,k; string checkLetter[20005]; scanf("%d %d %d",&n,&m,&k); for(int i=0;i<n;i++) { cin>>INM[i].num>>INM[i].letter; } delete_same(n); //sort(INM,INM+n,cmp); QSort(0,n-1); dictionary_palindromic(n); for(int i = 0;i<m;i++) { cin>>checkLetter[i]; check_suggest(checkLetter[i],n,k); if(i!=m-1)printf("\n"); } return 0; } /* 20 3 4 1827187200 the 1595609600 to 1107331800 that 401542500 this 334039800 they 282026500 their 250991700 them 196118888 these 150877900 than 144968100 time 125563600 then 109336600 two 196120000 there 87862100 those 79292500 through 75885600 the 71578000 think 67462300 2 65648600 tx356 57087700 though th xxx the */
- 优化算法(字典树)
#include<bits/stdc++.h> using namespace std; struct dot { char str[22]; int num; bool operator <= (const dot& other )const { if (num < other.num)return true; if (num > other.num)return false; if (str < other.str)return false; return true; } bool operator >= (const dot& other)const { if (num < other.num)return false; if (num > other.num)return true; if (str > other.str)return false; return true; } }; dot INM[10005]; struct Trie { bool flag = false; Trie *next[36] = {NULL}; int count = 0;//记录有多少个单词经过该节点 int sign[10];//存放经过该节点单词的下标 }; Trie * strasWith(Trie *root,char prefix[]) { Trie *node = root; for(int i =0;i<strlen(prefix);i++) { char c = prefix[i]; if(c<='9') { if(node->next[c-'0']== NULL) { return NULL; } node = node->next[c-'0']; } else { if(node->next[c-'a'+10]==NULL) { return NULL; } node = node->next[c-'a'+10]; } } return node; } void quickSort0(int L, int R) {//排序按照题目所给要求排序,概率大的放到前边概率相等的按照字母顺序排序 if (L < R) { dot t = INM[L]; int left = L, right = R; while (left<right) { while (INM[right] <= t && left < right) right--; INM[left] = INM[right]; while (INM[left] >= t && left < right) left++; INM[right] = INM[left]; } INM[left] = t; quickSort0(L, left - 1); quickSort0(left + 1, R); } } bool cmp(dot a,dot b) { if(a.num==b.num)return strcmp(a.str,b.str)<0; return a.num>b.num; } int main() { int index; int n, m, k; char s[25]; scanf("%d %d %d", &n, &m, &k); for (int i = 0;i < n;i++) { scanf("%d%s",&INM[i].num,INM[i].str); } sort(INM,INM + n,cmp); //quickSort0(0, n - 1); int R; Trie *root = new Trie; Trie *node ; for (int i = 0;i < n; i++) { node = root; //cout<<INM[index].str<<endl; //cout<<node->count<<endl; int len = strlen(INM[i].str); for(R =0;R<len;R++) { char c= INM[i].str[R]; if(c<='9') { index = c-'0'; } else { index = c-'a'+10; } if(node->next[index] == NULL) { break; } node = node->next[index]; } if(R==len){if(node->flag)continue;} node = root; for(int j =0;j<len;j++) { char c = INM[i].str[j]; if(c<='9') { index = c- '0'; } else { index = c- 'a' +10; } if (node->count < k)node->sign[node->count++] = i; if(node->next[index]==NULL) { node->next[index]= new Trie; } node = node->next[index]; //cout<<INM[index].str<<endl; //cout<<node->count<<endl; } node->flag = true; //单词的尾结点为true //node->num = num; } Trie* result; for (int i = 0;i < m;i++) { scanf("%s",s); result =strasWith(root,s); if (result == NULL || result->count == 0)printf("no suggestion\n"); else { for (int j = 0;j < result->count;j++) { printf("%s\n",INM[result->sign[j]].str); //cout<<INM[result->sign[j]].str<<endl;//输出 } } if(i!=m-1)printf("\n"); } } /* #include<bits/stdc++.h> using namespace std; struct dot { char str[25]; int num; bool operator <= (const dot& other )const { if (num < other.num)return true; if (num > other.num)return false; if (strcmp(str , other.str)<0)return false; return true; } bool operator >= (const dot& other)const { if (num < other.num)return false; if (num > other.num)return true; if (strcmp(str , other.str)>0)return false; return true; } }; dot INM[10005]; class Trie { private: bool flag = false; Trie *next[36] = {NULL}; int num = 0; int k;//被检测单词的序号 public: int count = 0;//记录有多少个单词经过该节点 int sign[10];//存放经过该节点单词的下标 //Trie Trie(int K){k= K;} //构造函数 void insert(char word[],int num,int index) { int in; Trie * node = this; for(int i =0;i<strlen(word);i++) { char c = word[i]; if(c<='9') { in = c-'0'; } else { in = c-'a'+10; } if (node->count < k)node->sign[node->count++] = index; if(node->next[in]==NULL) { node->next[in]= new Trie(k); } node = node->next[in]; // cout<<INM[index].str<<endl; //cout<<node->count<<endl; } node->flag = true; //单词的尾结点为true node->num = num; //cout<<num<<word<<endl; } bool search(char word[]) //查找单词是否存在于Trie { Trie *node =this; for(int i =0;i<strlen(word);i++) { int index; char c= word[i]; if(c<='9') { index = c-'0'; } else { index = c-'a'+10; } if(node->next[index] == NULL) { return false; } node = node->next[index]; } return node->flag; //如果该节点为串尾节点,则为true } Trie * strasWith(char prefix[]) { Trie *node = this; for(int i =0;i<strlen(prefix);i++) { int index; char c = prefix[i]; if(c<='9') { index= c-'0'; } else { index = c-'a'+10; } if(node->next[index]==NULL) { return NULL; } node = node->next[index]; } return node; } ~Trie(){delete next;} }; void quickSort0(int L, int R) {//排序按照题目所给要求排序,概率大的放到前边概率相等的按照字母顺序排序 if (L < R) { dot t = INM[L]; int left = L, right = R; while (left<right) { while (INM[right] <= t && left < right) right--; INM[left] = INM[right]; while (INM[left] >= t && left < right) left++; INM[right] = INM[left]; } INM[left] = t; quickSort0(L, left - 1); quickSort0(left + 1, R); } } Trie *build(int n,int k) { Trie *root; Trie *p = new Trie(k); root = p; for (int i = 0;i < n; i++) { if(!root->search(INM[i].str))//有重复的单词 ,不插入 { root->insert(INM[i].str,INM[i].num,i); } } return root;//返回根节点 } bool cmp(dot a,dot b) { if(a.num==b.num)return strcmp(a.str,b.str)<0; return a.num>b.num; } int main() { int n, m, k; int num; char checkLetter; scanf("%d %d %d", &n, &m, &k); for (int i = 0;i < n;++i) { scanf("%d%s",&INM[i].num,INM[i].str); } //sort(INM,INM + n,cmp); quickSort0(0, n - 1); Trie *root = build(n,k); Trie* result; for (int i = 0;i < m;i++) { char s[25]; scanf("%s",s); result = root->strasWith(s); if (result == NULL || result->count == 0)printf("no suggestion\n"); else { for (int j = 0;j < result->count;j++) { printf("%s\n",INM[result->sign[j]].str); //cout<<INM[result->sign[j]].str<<endl;//输出 } } if(i!=m-1)printf("\n"); } } */ /* 20 3 4 1827187200 the 1595609600 to 1107331800 that 401542500 this 334039800 they 282026500 their 250991700 them 196118888 these 150877900 than 144968100 time 125563600 then 109336600 two 196120000 there 87862100 those 79292500 through 75885600 the 71578000 think 67462300 2 65648600 tx356 57087700 though th xxx the */
以上代码均可直接运行(注释掉的部分也可直接调用,不过要看清楚哦!!!)