读论文进境缓慢...“水之积也不厚,故其负大舟也无力”,基础不够啊,最近要好好学一下《convex optimation》,按照stanford课程的节奏。此外要坚持刷题,先争取以后3天1题。
问题的网址:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1088
本题用循环单链表比较合适,data域是数值,由1到n组成循环单链表。然后对m,循环进行遍历和删除操作,直到找到一个m使得剩下的最后一个数字是2。顺便也复习了一下结构体,指针和数据结构。
附上最后AC的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <malloc.h> //也可用new和delete
using namespace std;
typedef struct node { //在c++里,struct前加typedef,则building是node别名,否则,building是node的一个实体。
node* next; //node前的struct可简略,但此处不能用buliding替代,因为别名后于定义。
int data;
}building;
building* head,*p,*q; // building等于struct node,不可写成struct building ; 若定义两个指针类型不要忘记第二个的*
void creat_queue(int n){
head->data = 1;
p = head;
for (int i = 2; i <= n; i++){
q = (building*)malloc(sizeof(building));
q->data = i;
q->next = head;
p->next = q;
p = q;
}
}
int find_last(int m){
p->next = head->next;
free(head);
head = p;
while (head->next != head){ //判别条件也可以根据n
for(int i = 1; i <m; i++ ){
head = head->next;
}
q = head->next;
head->next = q->next;
free(q);
}
return head->data;
}
int main()
{
int n, m;
head = (building*)malloc(sizeof(building)); //为新node分配空间用malloc,使用时再分配,注意malloc必须在函数内(?),收回(即删除)用free
while (1){
scanf("%d", &n);
if (n == 0)
break;
for (m = 2;; m++){
creat_queue(n);
if (find_last(m) == 2) //也可以用函数传参形式而不是全局变量head
break;
}
printf("%d\n", m);
}
return 0;
}
后记一:
本题其实是约瑟夫环问题!我之前的做法是模拟整个过程,时间复杂度为O(nm),但如果将之归纳为数学问题并推导,就可以给出复杂度为O(n)的解法。
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
每一轮,新起点为k=(m-1)%n,相当于做一次坐标变换x'=(x+k)%n,要得到n个人问题的解,只需得到n - 1个人问题的解,倒推下去。只有一个人时,胜利者就是编号0。所以递推公式为:
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)
胜利者的编号即为f[n]。
引用网上的AC代码:
#include<iostream>
using namespace std;
int n;
int check(int m)
{
int s=0;
for(int i=2; i<n; ++i)
s=(s+m)%i;
return s;
}
int main()
{
cin>>n;
while(n>0)
{
int m=1;
while(check(m)) ++m;
cout<<m<<endl;
cin>>n;
}
return 0;
}
后记二:
今天我又思考了一个引申的问题:
倒数第k个人出去的人编号是多少呢?
一样可以推公式,自己推了一下:
f[1]=m%k;
f[i]=(f[i-1]+m)%(k+i-1); (i>1)
胜利者的编号即为f[n-k+1]。