搜索
Curling 2.0(POJ 3009)
题目链接:Curling 2.0
参考博文:POJ 3009 Curling 2.0(DFS + 模拟)
- 题目大意:题意比较复杂,详见参考博文。
- 思路:重要的是理解清楚题意。障碍物可以撞碎,球在遇见障碍物之前一定是直行。DFS回溯方法比较适合。具体思路参考博文。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAX = 25;
int maze[MAX][MAX];
int dx[5] = {0, 1, 0, -1};
int dy[5] = {1, 0, -1, 0};
int ans;
int w, h;
int sx, sy;
//判断坐标的状态
int check(int x, int y)
{
if(maze[x][y]==0 || maze[x][y]==2) return 1;//可以移动
else if(maze[x][y]==-1 || maze[x][y]==1) return 2;//不能移动
else return 3;//到达目的地
}
void dfs(int x, int y, int t)
{
if(maze[x][y]==3 || t>10)//步数超出规定以及到达目的地均结束
{
if(ans>t) ans = t;
}
else
{
//四个方向遍历
for(int i=0; i<4; i++)
{
int tx = x, ty = y;
if(check(tx+dx[i], ty+dy[i]) != 2)//只进行可以移动的状态操作
{
while(check(tx+dx[i], ty+dy[i])==1)//如果前方可以移动,一直向同一个方向移动,步数不更新
tx += dx[i], ty += dy[i];
if(maze[tx+dx[i]][ty+dy[i]]==1)//前方障碍物
{
//当前方是障碍物时,撞碎变成0,更新了地图
maze[tx+dx[i]][ty+dy[i]] = 0;
t++;
dfs(tx, ty, t);//继续从障碍物前一个开始
t--;
maze[tx+dx[i]][ty+dy[i]] = 1;
}
else if(maze[tx+dx[i]][ty+dy[i]]==3)//到达目的地
{
t++;
dfs(tx+dx[i], ty+dy[i], t);//直接到达目的地,结束
t--;
}
}
}
}
}
int main()
{
while(scanf("%d%d", &w, &h)!=EOF && (w+h))
{
memset(maze, -1, sizeof(maze));
ans = 1<<29;
for(int i=1; i<=h; i++)
{
for(int j=1; j<=w; j++)
{
scanf("%d", &maze[i][j]);
if(maze[i][j]==2)
sx = i, sy = j;
}
}
dfs(sx, sy, 0);
if(ans>10)
printf("-1\n");
else
printf("%d\n", ans);
}
return 0;
}
Meteor Shower(POJ 3669)
题目链接:Meteor Shower
- 题目大意:给m个炸弹,炸弹在第t秒钟会在(x,y)处爆炸,爆炸会波及周围的四个点。求离(0,0)最近的没有被爆炸波及的点。
- 思路:广搜。将安全区标记为-1,将爆炸区标记为该点最早爆炸的时间点,bfs遍历爆炸区,目标到达任意一个安全区。注意一个点可能爆炸多次,需要去最早爆炸的时间。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAX = 500;
struct Node
{
int x, y, t;
Node(int x=-1, int y=-1, int t=-1): x(x), y(y), t(t){}
};
queue<Node> q;
int M, X, Y, T;
int vis[MAX][MAX];//-1代表安全区,0代表已经遍历的地区,其余正值代表爆炸的时间
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};
void bfs(Node s)
{
while(!q.empty()) q.pop();
q.push(s);
if(vis[s.x][s.y]<0)
{
printf("%d\n", &s.t);
return;
}
if(vis[s.x][s.y]>0 && s.t<vis[s.x][s.y])
vis[s.x][s.y] = 0;
while(!q.empty())
{
Node u = q.front(); q.pop();
for(int i=0; i<4; i++)
{
int x = u.x+dx[i], y = u.y+dy[i], t = u.t+1;
if(x>=0 && y>=0)
{
if(vis[x][y]>0 && t<vis[x][y])
{
vis[x][y] = 0;
q.push(Node(x, y, t));
}
else if(vis[x][y]<0)
{
printf("%d\n", t);
return;
}
}
}
}
printf("-1\n");
}
int main()
{
scanf("%d", &M);
memset(vis, -1, sizeof(vis));
for(int i=0; i<M; i++)
{
scanf("%d%d%d", &X, &Y, &T);
//同一个点可能会爆炸多次,取最早爆炸的一次
if(vis[X][Y]==-1)
vis[X][Y] = T;
else if(vis[X][Y]>T)
vis[X][Y] = T;
for(int j=0; j<4; j++)
{
int x = X+dx[j], y = Y+dy[j];
if(x>=0 && y>=0)
{
if(vis[x][y]==-1)
vis[x][y] = T;
else if(vis[x][y]>T)
{
vis[x][y] = T;
}
}
}
}
bfs(Node(0, 0, 0));
return 0;
}
Smallest Difference(POJ 2718)
题目链接:Smallest Difference
- 题目大意:将k个数字分成两份,组成两个整数,求其最小的差值。
- 思路:全排列的求解搜索。因为最多是0-9,所以如果全排列可知有428800中情况,不超时。注意nex_permutation(a, a+k)函数的使用(使用do循环)。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
int T;
string s;
int a[15];
int b, c;//b是大的那一方
cin >> T;
getchar();
while(T--)
{
getline(cin, s);
int k = 0, cnt;
for(int i=0; i<s.size(); i++)
{
if(s[i]!=' ')
{
a[k++] = s[i]-'0';
}
}
//要排好第一个排列(升序)
sort(a, a+k);
int Min = 1<<29;
do
{
b = c = 0;
if(a[0]==0 || (a[k/2]==0 && k>2)) continue;//舍去这个排列
for(int i=0; i<k/2; i++)
b = b*10+a[i];
for(int i=k/2; i<k; i++)
c = c*10+a[i];
cnt = abs(b-c);
Min = min(Min, cnt);
}while(next_permutation(a, a+k));//获取下一个排序
cout << Min << endl;
}
return 0;
}
Hopscotch(POJ 3050)
题目链接:Hopscotch
- 题目大意:牛可以在一个矩阵上任意一点上向前后左右四个方向跳格子,跳五次后得到含有六个数字的数字串,计算其组成的整数,求所得整数的可能的个数。
- 思路:穷竭搜索。注意使用set结构体来存储枚举得到的结果,这样可以省去很多代码。遍历每一个点起始得到的整数,存入Set,最后得到set的大小。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int MAX = 10;
struct Node
{
int x, y;
Node(int x=-1, int y=-1): x(x), y(y){}
};
int G[MAX][MAX];
int r[100000][MAX], n;
set<int> a;
Node b[MAX];
//右、左、下、上
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};
int cnt = 0;
void dfs(Node s, int step, int num)
{
if(step==5)
{
a.insert(num);
return;
}
for(int i=0; i<4; i++)
{
int x = dx[i]+s.x, y = dy[i]+s.y, t = step+1;
if(x>=0 && x<5 && y>=0 && y<5)
{
dfs(Node(x, y), t, num*10+G[x][y]);
}
}
return;
}
int main()
{
n = 0;
for(int i=0; i<5; i++)
{
for(int j=0; j<5; j++)
{
scanf("%d", &G[i][j]);
}
}
for(int i=0; i<5; i++)
{
for(int j=0; j<5; j++)
{
dfs(Node(i, j), 0, G[i][j]);
}
}
printf("%d\n", a.size());
return 0;
}
贪心
Cleaning Shifts(POJ 2376)
题目链接:Cleaning Shifts
- 题目大意:要求使用最少的区间覆盖一个大区间。
- 思路:贪心。需要在可以首尾相接的条件下,保证得到的区间覆盖最远的距离。要注意首尾相接的条件可以不重合,但需要接上。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 25005;
struct Node
{
int s, e;
bool operator < (const Node &A) const
{
return s<A.s;
}
};
Node c[MAX];
int main()
{
int N, T;
scanf("%d%d", &N, &T);
for(int i=0; i<N; i++)
{
scanf("%d%d", &c[i].s, &c[i].e);
}
sort(c, c+N);
int start = 0, end = 0, cnt = 0, flag, ans = 1;
for(int i=0; i<N; i++)
{
flag = 0;
//一定要注意是end+1
if(c[i].s<=end+1 && c[i].e>cnt)//起始点可以接上,终点取满足起始点条件的最大值
{
cnt = c[i].e;
}else if(c[i].s>end+1 && c[i].s<=cnt+1 && c[i].e>cnt)//有新的更新目标(即起始点接不上,但满足更新条件)
{
end = cnt;
ans++;
i--;//注意这种情况需要重来一次
}
if(cnt==T)//满足终止条件
{
flag = 1;
break;
}
}
if(flag)
printf("%d\n", ans);
else
printf("-1\n");
return 0;
}
Radar Installation(POJ 1328)
题目链接:Radar Installation
参考博文:Radar Installation
- 题目大意:在坐标系中,x轴上方为海洋(海洋中有多个岛屿,以点标记),x轴下方为陆地。需要在陆地上建立多个雷达站,使得雷达探测范围可以将所有岛屿覆盖,求需要建立的雷达站的最少数目。
- 思路:比较好的贪心算法题目。将思想转换一下,计算探测到每一个岛屿的雷达站建设区域,在重合区域内建立雷达站即可探测到多个岛屿。需要记录区域的起始和终止,按起始点从小到大排序,然后不断更新重叠区域的终止点(可能变近),如果没有重叠区域则加一(起始点比重叠区域的终止点还大)。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1005;
struct Node
{
double s, e;
bool operator < (const Node &A) const
{
return s<A.s;
}
};
Node a[MAX];
int n, d, x, y;
int main()
{
int Case = 1;
while(scanf("%d%d", &n, &d)!=EOF)
{
if(n==0 && d==0)
break;
int flag = 0;
for(int i=0; i<n; i++)
{
scanf("%d%d", &x, &y);
if(y<=d)
{
double bias = sqrt(d*d-y*y);
a[i].s = x-bias, a[i].e = x+bias;
}
else
{
flag = 1;
}
}
printf("Case %d: ", Case++);
if(flag)
{
printf("-1\n");
continue;
}
//n不能是double类型,只能是int
sort(a, a+n);
int ans = 1;
//注意使用double类型
double start = a[0].s, end = a[0].e;
for(int i=1; i<n; i++)
{
if(a[i].s>end)//没有交集
{
ans++;
//start = a[i].s;
end = a[i].e;
}else if(a[i].e<=end)//交集缩小
{
//start = a[i].s;
end = a[i].e;
}
}
printf("%d\n", ans);
}
return 0;
}
Stall Reservations(POJ 3190)
题目链接:Stall Reservations
- 题目大意:每个牛在某个区间工作,需要占用一个牛棚,问至少需要准备多少牛棚,并给出一个分配方案。
- 思路:贪心加上优先级队列的题。按照牛区间起始点从小到大排序并遍历,如果已经使用的牛棚的使用区间终止点比遍历的牛的区间起始点低,则该牛可以进入该牛棚工作,否则,新开一个牛棚。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int MAX = 50005;
int N, s, t, ans[MAX];
struct Node
{
int s, e, id;//起始点,终止点,编号(用来当下标)
Node(int s = -1, int e = -1, int id = -1): s(s), e(e), id(id){}
};
bool cmp1(Node A, Node B)
{
return A.s<B.s;
}
struct cmp2
{
bool operator () (Node &A, Node &B)
{
return A.e>B.e;
}
};
Node cows[MAX];
Node stall[MAX];
priority_queue<Node, vector<Node>, cmp2> q;//用来筛选stall(选择终止点最小的)
int main()
{
while(scanf("%d", &N)!=EOF)
{
for(int i=0; i<N; i++)
{
scanf("%d%d", &cows[i].s, &cows[i].e);
cows[i].id = i+1;
}
sort(cows, cows+N, cmp1);//起始点越靠前的越在前面
int k = 1;
ans[cows[0].id] = 1;//记录牛被分到了哪一个stall中
q.push(Node(cows[0].s, cows[0].e, 1));
for(int i=1; i<N; i++)
{
//比较最小终止点是否小于下一个起始点
if(cows[i].s<=q.top().e)
{
k++;
ans[cows[i].id] = k;
q.push(Node(cows[i].s, cows[i].e, k));
}else
{
Node t = q.top(); q.pop();
t.e = cows[i].e;
ans[cows[i].id] = t.id;
q.push(t);
}
}
printf("%d\n", k);
for(int i=1; i<=N; i++)
{
printf("%d\n", ans[i]);
}
while(!q.empty()) q.pop();
}
return 0;
}
Yogurt factory(POJ 2393)
题目链接:Yogurt factory
- 题目大意:有一个奶酪工厂,给出这个工厂每天加工每个奶酪需要的价格,以及每天的需求量,另外,奶酪也可以存放在仓库里(竟然放不坏!),给出每个奶酪存放一天需要的成本,问,这些生产任务全部完成,最少的花费是多少。
- 思路:这道题的贪心策略比较巧妙。可以直接从前向后更新每一天的奶酪生产价格(包括存储成本)。即:更新当前周的价格:min(当前周价格,上一周的价格+S),更新后则直接计算即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAX = 10005;
int C[MAX], Y[MAX];
int N, S;
LL cost;
int main()
{
scanf("%d%d", &N, &S);
for(int i=0; i<N; i++)
scanf("%d%d", &C[i], &Y[i]);
cost = 0;
//更新当前周的价格:min(当前周价格,上一周的价格+S)
for(int i=1; i<N; i++)
{
C[i] = min(C[i], C[i-1]+S);
}
//计算值
for(int i=0; i<N; i++)
{
cost += C[i]*Y[i];
}
printf("%lld\n", cost);
return 0;
}
Packets(POJ 1017)
题目链接:Packets
参考博文:POJ1017 Packets(贪心算法训练)
- 题目大意: 公司共有底面面积为11、22、33、44、55、66,高度同为H的六种产品,现在需要用最少的箱子打包,箱子的底面面积为6*6,高度为H。
- 思路:先装大的后装小的,空隙之间可能装小的,尽量填满空隙。因此需要从大到小以此模拟装入,具体参考博文。
代码:
#include <iostream>
#include <cstdio>
using namespace std;
int a1, a2, a3, a4, a5, a6, s3, s2;
int main()
{
while(scanf("%d%d%d%d%d%d", &a1, &a2, &a3, &a4, &a5, &a6)!=EOF)
{
if(a1+a2+a3+a4+a5+a6==0) break;
int p = 0;
p += a6/1;
p += a5/1;
a1 = max(a1-a5*11, 0);
p += a4/1;
if(a2<5*a4) a1 = max(a1-(5*a4-a2)*4, 0);
a2 = max(a2-5*a4, 0);
p += a3/4;
s3 = a3%4;
if(s3) p++;
if(s3==1)
{
if(a2<5) a1 = max(a1-(5-a2)*4-7, 0);
else a1 = max(a1-7, 0);
a2 = max(a2-5, 0);
}
else if(s3==2)
{
if(a2<3) a1 = max(a1-(3-a2)*4-6, 0);
else a1 = max(a1-6, 0);
a2 = max(a2-3, 0);
}
else if(s3==3)
{
if(a2<1) a1 = max(a1-(1-a2)*4-5, 0);
else a1 = max(a1-5, 0);
a2 = max(a2-1, 0);
}
p += a2/9;
s2 = a2%9;
if(s2!=0) p++;
if(s2) a1 = max(a1-(36-4*s2), 0);
p += (a1+35)/36;
printf("%d\n", p);
}
return 0;
}
Allowance(POJ 3040)
题目链接:Allowance
参考博文:Allowance(POJ-3040)
- 题目大意:每周至少给牛 c 元,有 n 种硬币,已知每种硬币币值和数量,求最多坚持的周数。
- 思路:将硬币从小到大排序,大于等于 c 元的硬币直接支付,然后将不能直接支付的先从大到小凑到接近 c ,再从小到大向上加,如果能达到就记录并开始下一轮,如果不能就退出。要注意的是,硬币无法拆分,因此只能多给不能少给。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 1005;
const int INF = 1<<29;
struct Node
{
int num, value;
bool operator < (const Node &A) const
{
return value < A.value;
}
};
int use[MAX];//记录硬币一周使用的个数
int n, c, cnt;
int main()
{
cnt = 0;
scanf("%d%d", &n, &c);
for(int i=1; i<=n; i++)
scanf("%d%d", &coin[i].value, &coin[i].num);
sort(coin+1, coin+1+n);
//找出硬币币值大于c元的数目
for(int i=1; i<n; i++)
{
if(coin[i].value>=c)
{
cnt += coin[i].num;//直接支付
coin[i].num = 0;//清零
}
}
while(true)
{
bool flag = false;
int sum = c;
memset(use, 0, sizeof(use));
//从大到小逼近c
for(int i=n; i>0; i--)
{
if(coin[i].num>0)
{
use[i] = min(sum/coin[i].value, coin[i].num);
sum -= use[i]*coin[i].value;
if(sum==0)
{
flag = true;
break;
}
}
}
//从小到大抵达或略微超过c
if(sum>0)
{
for(int i=1; i<=n; i++)
{
if(coin[i].num-use[i]>0)
{
while(use[i]<coin[i].num)
{
sum -= coin[i].value;
use[i]++;
if(sum<=0)
{
flag = true;
break;
}
}
}
if(flag) break;
}
}
if(!flag) break;
//计算上面得出的方案最多可以进行几周
int minn = INF;
for(int i=1; i<=n; i++)
{
if(use[i]>0)
minn = min(minn, coin[i].num/use[i]);//记录所有硬币中最小的使用周数
}
for(int i=1; i<=n; i++)
{
if(use[i]>0)//按照求出的使用周数,更新硬币的数目
coin[i].num -= minn*use[i];
}
cnt += minn;
}
printf("%d\n", cnt);
return 0;
}
Stripies(POJ 1862)
题目链接:Stripies
- 题目大意:从N个数任取两个数按2 × \times ×sqrt(a*b)合成新数放回,求最后那个数的最小值。
- 思路:贪心策略是使尽量使大的数多参与开方运算。每次取出最大和次大的数字运算,直至剩下一个数字。因为运算公式的原因,所以运算之后的结果一定是最大的数字,所以可以简化计算,直接排序之后遍历计算。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
const int MAX = 200;
bool cmp(int a, int b)
{
return a>b;
}
int main()
{
int n, m[MAX];
cin >> n;
for(int i=0; i<n; i++)
cin >> m[i];
sort(m, m+n, cmp);//从大到小排序
double b = m[0];
for(int i=1; i<n; i++)
{
b = 2*sqrt(b*m[i]);
}
printf("%.3f", b);
}
Protecting the Flowers(POJ 3262)
- 题目大意:n头牛在吃花,每头牛牵走(避免吃花)要花费2*t秒(来回),每头牛吃花的速率是d,要你自己设计一个牵走牛的顺序,使被牛吃的花最少。
- 思路:贪心算法。其实就是按d/t从大到小排序,然后依次牵走。重点是可能超时,需要在代码优化上下点功夫。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAX = 100005;
struct Node
{
int t, d;
};
bool cmp(Node a, Node b)
{
double ap = a.d/(double)a.t;
double bp = b.d/(double)b.t;
return ap>bp;
}
Node C[MAX];
LL ans = 0, s = 0;
int main()
{
int N, T, D;
while(scanf("%d", &N)!=EOF)
{
for(int i=0; i<N; i++)
{
scanf("%d%d", &C[i].t, &C[i].d);
s += C[i].d;//先加上,后减去,降低了复杂度
}
sort(C, C+N, cmp);
for(int i=0; i<N-1; i++)
{
s -= C[i].d;
ans += s*C[i].t*2;
}
printf("%lld\n", ans);
}
return 0;
}
动态规划
Sumsets(POJ 2229)
题目链接:Sumsets
参考博文:动态规划之划分数
DP:Sumsets(POJ 2229)
- 题目大意:给一个数,将其进行分解,只能分解为2的幂次,最多能分解多少种。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 1000001;
int dp[MAX];
int c;
int main()
{
int N;
while(cin >> N)
{
dp[0] = 1;
c = 1;
for(int i=1; i<=20 && c<=N; i++)
{
for(int j=c; j<=N; j++)
{
dp[j] = (dp[j]+dp[j-c])%1000000000;
}
c = c*2;//累乘,节省了时间
}
cout << dp[N] << endl;
}
return 0;
}
Milking Time(POJ 3616)
题目链接:Milking Time
参考博文:poj 3616 Milking Time —DP(带权重的区间动态规划)
- 题目大意:题意就是有m头牛,每一只牛有一个产奶的时间段和一个奶量,Bessie可以去给每一头奶牛挤奶,但是每次给一个奶牛挤奶之后必须休息R分钟,问Bessie选择怎么挤奶可以使挤出的奶最多。
- 思路:
- 状态构造:dp[i]代表在前i个区间可以获得的最大奶量。
- 终态:dp[M],M是牛的数量。
- 状态转移方程:dp[i] = max(dp[i-1], dp[p[i]]+w[i](其中p[i]表示在第i个区间之前最近的一个与第i个区间不冲突的区间的下标,w[i]表示第i个区间的奶量。
- 初始化:dp[0] = 0;
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 1000001;
struct Node
{
int s, e, v;
bool operator < (const Node &A) const
{
return e<A.e;
}
};
Node C[1005];
int dp[1005];
int p[1005];//上一个不重合可使用区间
int N, M, R, start, end, eff;
bool check(Node a, Node b)
{
if(a.s>=b.e+R || b.s>=a.e+R) return 1;
else return 0;
}
void Calculate_P(int M, int R)
{
p[1] = 0;
for(int i=M; i>1; i--)
{
int k=i-1;
while(k>0 && !check(C[k], C[i]))
k--;
p[i] = k;
}
}
int main()
{
while(scanf("%d%d%d", &N, &M, &R)!=EOF)
{
for(int i=1; i<=M; i++)
{
scanf("%d%d%d", &C[i].s, &C[i].e, &C[i].v);
}
sort(C+1, C+M+1);//按结束点排序
Calculate_P(M, R);//计算每一个区间前面可以接着的最近区间下标
memset(dp, 0, sizeof(dp));
for(int i=1; i<=M; i++)
{
dp[i] = max(dp[i-1], dp[p[i]]+C[i].v);//第i个区间是否使用
}
printf("%d\n", dp[M]);
}
return 0;
}
数据结构
Sunscreen(POJ 3614)
题目链接:Sunscreen
参考博文:Sunscreen (poj 3614 贪心+优先队列)
- 题目大意:有c头牛晒太阳,每头牛都有一个能承受辐射的范围(min~max),现在有 l 种防晒霜,每种防晒霜都能将辐射值固定在spf,每种防晒霜都有一定的数量num。每头牛用最多一种防晒霜,问最多能满足多少头牛。
- 思路:先将防晒霜按照spf从小到大排序,再将牛按照min从小到大排序,最后按序取出防晒霜,如果牛的min小于等于spf则入队列,将所有满足条件的牛入队列,然后找出其max最小的牛且max>=spf。循环以上步骤直到防晒霜用完或牛遍历完。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int MAX = 2505;
struct Sun
{
int spf, num;
};
struct Cow
{
int min, max;
};
struct cmp
{
bool operator ()(Cow a, Cow b)
{
return a.max>b.max;
}
};
bool cmp1(Cow a, Cow b)
{
return a.min<b.min;
}
bool cmp2(Sun a, Sun b)
{
return a.spf<b.spf;
}
priority_queue<Cow, vector<Cow>, cmp> Q;
Cow Cow[MAX], p;
Sun S[MAX];
int main()
{
int C, L;
cin >> C >> L;
for(int i=0; i<C; i++)
cin >> Cow[i].min >> Cow[i].max;
for(int i=0; i<L; i++)
cin >> S[i].spf >> S[i].num;
sort(Cow, Cow+C, cmp1);
Cow[C].min = 1<<29, Cow[C].max = 1<<29;//哨点
sort(S, S+L, cmp2);
int k = 0, ans = 0;
for(int i=0; i<=C; i++)
{
if(k==L) break;
if(Cow[i].min<=S[k].spf)
Q.push(Cow[i]);
else if(!Q.empty())//符合该防晒霜的牛已经全部进队列
{
p = Q.top();
Q.pop();
while(p.max<S[k].spf && !Q.empty())
{
p = Q.top(); Q.pop();
}
if(p.max>=S[k].spf)
{
S[k].num--;
if(S[k].num==0)
k++;
ans++;
}
i--;
}
else if(Q.empty())//该防晒霜不能用,换下一个
{
k++, i--;
}
}
cout << ans << endl;
return 0;
}
Moo University - Financial Aid(POJ 2010)
题目链接:Moo University - Financial Aid
参考博文:POJ 2010 Moo University – Financial Aid 题解 《挑战程序设计竞赛》
- 题目大意:从C头奶牛中招收N头。它们分别得分score_i,需要资助学费aid_i。希望新生所需资助不超过F,同时得分中位数最高。求此中位数。
- 思路:先将奶牛排序,考虑每个奶牛作为中位数时,比它分数低(前面的)的那群牛的学费总和lower_i(选取N/2个最少学费之和),后面的总和upper_i(选取N/2个最少学费之和)。然后从分数高往分数低扫描,满足aid_i + lower_i + upper_i <= F的第一个解就是最优解。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int MAX = 100005;
struct Cow
{
int s, m;
};
bool cmp(Cow a, Cow b)
{
return a.s>b.s;
}
struct cmp1
{
bool operator ()(Cow a, Cow b)
{
return a.m<b.m;
}
};
int N, C, F;
Cow c[MAX];
int lower[MAX], upper[MAX];//在前i个或后i个牛中最少的N/2个学费之和
priority_queue<Cow, vector<Cow>, cmp1>q, p;//取最大的学费
int main()
{
while(scanf("%d%d%d", &N, &C, &F)!=EOF)
{
for(int i=0; i<C; i++)
scanf("%d%d", &c[i].s, &c[i].m);
sort(c, c+C, cmp);//分数降序
int n = N/2, total = 0;
//求lower的值
for(int i=0; i<C; i++)
{
if(q.size()>=n)
{
lower[i] = total;
}else
{
lower[i] = 0;
}
q.push(c[i]);
total += c[i].m;
if(q.size()>n)
{
total -= q.top().m;
q.pop();
}
}
//求upper的值
total = 0;
for(int i=C-1; i>=0; i--)
{
if(p.size()>=n)
{
upper[i] = total;
}else
{
upper[i] = 0;
}
p.push(c[i]);
total += c[i].m;
if(p.size()>n)
{
total -= p.top().m;
p.pop();
}
}
//筛选出最终结果
int ans = -1;
for(int i=C; i>=0; i--)
{
if(lower[i]+c[i].m+upper[i]<=F)//判断条件
{
ans = c[i].s;
break;
}
}
if(ans==-1)
printf("-1\n");
else
printf("%d\n", ans);
}
return 0;
}
并查集
Wireless Network
题目链接:Wireless Network
- 题目大意:一些结点需要维修,并检查一些结点是否连通。给出结点数目,结点之间的连通半径,并给出所有结点的坐标,最后给出相应操作,维修和检验联通,每一次检验均需要给出检验结果。
- 思路:并查集题。在维修时,判断该结点是否可以与之间维修的结点并入一个集合(即连通),检验时直接用并查集检验。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;
const int MAX = 1005;
struct Node
{
int x, y;
};
Node C[MAX];
int a[MAX], d, N, id, id2;
int pre[MAX], Rank[MAX];
char order;
bool check(int x1, int y1, int x2, int y2)
{
if(sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<=(double)d)
{
return true;
}
return false;
}
void Init()
{
for(int i=0; i<MAX; i++)
{
pre[i] = i;
Rank[i] = 1;
}
}
int findRoot(int x)
{
int r = x;
while(r!=pre[r])
{
r = pre[r];
}
int j=x, i;
while(j!=r)
{
i = pre[j];
pre[j] = r;
j = i;
}
return r;
}
void join(int x, int y)
{
int fx = findRoot(x), fy = findRoot(y);
if(Rank[fx]>Rank[fy])
{
pre[fy] = fx;
}else
{
pre[fx] = fy;
if(Rank[fx]==Rank[fy]) Rank[fy]++;
}
}
bool same(int x, int y)
{
return findRoot(x)==findRoot(y);
}
int main()
{
scanf("%d%d", &N, &d);
Init();
for(int i=1; i<=N; i++)
{
scanf("%d%d", &C[i].x, &C[i].y);
}
int k = 0;
while(scanf("%c%d", &order, &id)!=EOF)
{
if(order=='O')
{
a[k++] = id;
for(int i=0; i<k-1; i++)
{
if(check(C[a[i]].x, C[a[i]].y, C[id].x, C[id].y))
{
join(id, a[i]);
}
}
}
else if(order=='S')
{
scanf("%d", &id2);
if(same(id, id2))
{
//cout << pre[id] << " " << pre[id2] << endl;
printf("SUCCESS\n");
}else
{
printf("FAIL\n");
}
}
}
return 0;
}
Find them, Catch them(POJ 1703)
- 题目大意:在一个城市里有两种不同的犯罪团伙。首先输入T表示有T组测试,然后输入N和M,表示有N个罪犯(编号从1到N)而且接下来有M个操作。操作分为两种:
1.D a b,表示编号为a和b的两个罪犯属于不同的犯罪团伙;
2.A a b,表示询问编号为a和b的两个罪犯是否是同一个犯罪团伙或者不确定。
对于每一个A操作,根据题意都要有相应的回答(输出)。 - 思路:这道题目类似食物链的题目食物链(相关讲解),属于种类并查集,即无法准确确定一个类具体属于那一个种类,只能知道不同类之间的关系,这样就需要维护比原始数目大种类个数倍的数组,同时更新每一个每一个阶段的数组,维护其关系而不是维护其种类。
本题目的并查集表示A和B分别属于两个集团的关系同时存在。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int MAX = 100005;
int pre[MAX*2], Rank[MAX*2];
int N = MAX-1;
//并查集表示关系同时存在
void Init()
{
for(int i=0; i<=2*MAX; i++)
{
pre[i] = i;
Rank[i] = 1;
}
}
int findRoot(int x)
{
int r = x;
while(r!=pre[r])
{
r = pre[r];
}
int j=x, i;
while(j!=r)
{
i = pre[j];
pre[j] = r;
j = i;
}
return r;
}
void join(int x, int y)
{
int fx = findRoot(x), fy = findRoot(y);
if(Rank[fx]>Rank[fy])
{
pre[fy] = fx;
}else
{
pre[fx] = fy;
if(Rank[fx]==Rank[fy]) Rank[fy]++;
}
}
bool same(int x, int y)
{
return findRoot(x)==findRoot(y);
}
int main()
{
int T, N, M, a, b;
char order;
scanf("%d", &T);
while(T--)
{
Init();
scanf("%d%d", &N, &M);
for(int i=0; i<M; i++)
{
getchar();
scanf("%c%d%d", &order, &a, &b);
if(order=='D')
{
join(a, b+N);
join(a+N, b);
}else if(order=='A')
{
if(same(a, b))
printf("In the same gang.\n");
else if(same(a, b+N) || same(a+N, b))//不是一个种类
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
}
}
return 0;
}
图论
Six Degrees of Cowvin Bacon(POJ 2139)
题目链接:Six Degrees of Cowvin Bacon
- 题目大意:先根据题意建立图,然后求出每个点到其他每个点的最短距离的均值(即总和/可以到达的点的个数)
- 思路:使用的多源最短路径中的Floyd算法。得到了所有结点之间的最短距离后,遍历每一个点的均值,然后得到最小的值就是结果。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 500;
const int INF = 1<<29;
int G[MAX][MAX], a[MAX];
int cnt = 0, Min = INF, ans;
int N, M, n;
int main()
{
scanf("%d%d", &N, &M);
for(int i=0; i<=N; i++)
{
for(int j=0; j<=N; j++)
G[i][j] = INF;
}
while(M--)
{
scanf("%d", &n);
for(int i=0; i<n; i++)
{
scanf("%d", &a[i]);
for(int j=i-1; j>=0; j--)
{
G[a[j]][a[i]] = 1;
G[a[i]][a[j]] = 1;
}
}
}
for(int k=1; k<=N; k++)
{
for(int i=1; i<=N; i++)
{
if(G[i][k]==INF) continue;
for(int j=1; j<=N; j++)
{
if(G[k][j]==INF) continue;
G[i][j] = min(G[i][j], G[i][k]+G[k][j]);
}
}
}
Min = INF;
double r;
for(int i=1; i<=N; i++)
{
cnt = 0, ans = 0;
for(int j=1; j<=N; j++)
{
if(i==j) continue;
if(G[i][j]!=INF)
{
ans++;
cnt += G[i][j];
}
}
Min = min(Min, cnt);
//cout << cnt << endl;
r = Min/(1.0*ans)*100;
}
Min = r;
printf("%d\n", Min);
return 0;
}
Wormholes(POJ 3259)
题目链接:Wormholes
- 题目大意:农夫john发现了一些虫洞,虫洞是一种在你到达虫洞之前把你送回目的地的一种方式,FJ的每个农场,由n块土地(编号为1-n),M条路,和W个 虫洞组成,FJ想从一块土地开始,经过若干条路和虫洞,返回到他最初开始走的地方并且时间要在他离开之前,或者恰好等于他离开的时间。
- 思路:明显的使用带负权的单源最短路径算法,所有选择Bellman_Ford算法,最后判断一下是否有负环而已。裸题。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 505;
const int INF = 1<<29;
int G[MAX][MAX], d[MAX];
int F, N, M, W, En, S, E, T;
struct Edge
{
int from, to, w;
}e[5005];
void Bellman_Ford(int s)
{
fill(d, d+MAX, INF);
d[s] = 0;
int k = 0;
while(1)
{
int flag = 0;
for(int i=0; i<En; i++)
{
if(d[e[i].from]!=INF && d[e[i].to]>d[e[i].from]+e[i].w)
{
d[e[i].to] = d[e[i].from]+e[i].w;
flag = 1;
}
}
if(!flag) break;
k++;
if(k>=N) break;
}
if(k>=N)
printf("YES\n");
else
printf("NO\n");
}
int main()
{
scanf("%d", &F);
while(F--)
{
En = 0;
scanf("%d%d%d", &N, &M, &W);
for(int i=0; i<M; i++)//双向
{
scanf("%d%d%d", &S, &E, &T);
e[En].w = T, e[En].from = S, e[En++].to = E;
e[En].w = T, e[En].from = E, e[En++].to = S;
}
for(int i=0; i<W; i++)//单向
{
scanf("%d%d%d", &S, &E, &T);
e[En].w = -T, e[En].from = S, e[En++].to = E;
}
Bellman_Ford(1);
}
return 0;
}
Silver Cow Party(POJ 3268)
题目链接:Silver Cow Party
- 题目大意:给定一些路线和花费,然后N头牛,每头牛要求按照最短路走到X处,并用最短路在返回,问这些牛中所有路线里最大值是多少。(注意是单向图)
- 思路:可以先从X用Dijkstra,再将图的边的方向反过来,再用dijstra求一下X的单源最短路径,求二者和最大即可
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAX = 1005;
const int INF = 1<<29;
int G[MAX][MAX];
int d[MAX], d2[MAX], vis[MAX];
int N, M, X, A, B, T;
void dijstra(int s, int (&d)[MAX])
{
fill(d, d+N+2, INF);
memset(vis, 0, sizeof(vis));
d[s] = 0;
while(1)
{
int minv = INF, u;
for(int i=1; i<=N; i++)
{
if(vis[i]==0 && d[i]<minv)
{
minv = d[i];
u = i;
}
}
if(minv==INF) break;
vis[u] = 1;
for(int j=1; j<=N; j++)
{
if(vis[j]==0 && G[u][j]!=INF && d[u]+G[u][j]<d[j])
{
d[j] = G[u][j] + d[u];
}
}
}
}
int main()
{
scanf("%d%d%d", &N, &M, &X);
for(int i=0; i<=N; i++)
{
for(int j=0; j<=N; j++)
{
G[i][j] = INF;
}
}
for(int i=0; i<M; i++)
{
scanf("%d%d%d", &A, &B, &T);
G[A][B] = T;
}
dijstra(X, d);
for(int i=1; i<=N; i++)
{
for(int j=1; j<=i; j++)
{
swap(G[i][j], G[j][i]);
}
}
dijstra(X, d2);
int s = 0;
for(int i=1; i<=N; i++)
{
if(d[i]!=INF && d2[i]!=INF)
s = max(d[i]+d2[i], s);
}
printf("%d\n", s);
return 0;
}
Bad Cowtractors(POJ 2377)
题目链接:Bad Cowtractors
- 题目大意:给定每条路线间需要的费用,建造一个最贵的网络线路(任意两节点都可以互相达到,但是不存在回路),求最多需要花费多少钱。
- 思路:求最大生成树。则将边的权重取负,然后按照最小生成树计算即可。
注意:使用邻接表可以方便处理结点之间有重边的情况,并且效率还高,推荐使用邻接表实现。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 1005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{
int to, w;
Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];
void Prim()
{
fill(d, d+MAX, INF);
memset(vis, 0, sizeof(vis));
d[1] = 0;
while(1)
{
int Minv = INF, u;
for(int i=1; i<=N; i++)
{
if(vis[i]==0 && d[i]<Minv)
{
Minv = d[i];
u = i;
}
}
if(Minv==INF) break;
vis[u] = 1;
//cout << u << " " << d[u] << endl;
for(int j=0; j<G[u].size(); j++)
{
Node v = G[u][j];
if(vis[v.to]==0 && d[v.to]>v.w)
{
d[v.to] = v.w;
}
}
}
int sum = 0, flag = 0;
//cout << endl;
for(int i=1; i<=N; i++)
{
if(d[i]!=INF)
sum += d[i];
else
flag = 1;
}
if(!flag)
printf("%d\n", -sum);
else
printf("-1\n");
}
int main()
{
cin >> N >> M;
for(int i=0; i<M; i++)
{
cin >> A >> B >> C;
G[A].push_back(Node(B, -C));
G[B].push_back(Node(A, -C));
}
Prim();
return 0;
}
Out of Hay(POJ 2395)
题目链接:Out of Hay
- 题目大意:有n个农场,贝西在1号农场,要访问其他n-1个农场,给出m条路,a b c表示a农场到b农场路程为c(两个农场间可能有多条路)。贝西会挑选最短的路径来访问完这n-1个农场。 问在此过程中贝西会经过的最大边是多大?
- 思路:求最小生成树上的最大边,注意有重边,使用邻接表。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
/*
使用了邻接表,不需要考虑重边,因为所以的边均保存了;
如果是邻接矩阵,需要保存最大或最小的边
*/
const int MAX = 2005;
const int INF = 1<<29;
int N, M;
int A, B, C;
int vis[MAX], d[MAX];
struct Node
{
int to, w;
Node(int to=-1, int w=-1): to(to), w(w){}
};
vector<Node>G[MAX];
void Prim()
{
fill(d, d+MAX, INF);
memset(vis, 0, sizeof(vis));
d[1] = 0;
while(1)
{
int Minv = INF, u;
for(int i=1; i<=N; i++)
{
if(vis[i]==0 && d[i]<Minv)
{
Minv = d[i];
u = i;
}
}
if(Minv==INF) break;
vis[u] = 1;
//cout << u << " " << d[u] << endl;
for(int j=0; j<G[u].size(); j++)
{
Node v = G[u][j];
if(vis[v.to]==0 && d[v.to]>v.w)
{
d[v.to] = v.w;
}
}
}
int Max = 0;
//cout << endl;
for(int i=1; i<=N; i++)
{
Max = max(Max, d[i]);
}
printf("%d\n", Max);
}
int main()
{
cin >> N >> M;
for(int i=0; i<M; i++)
{
cin >> A >> B >> C;
G[A].push_back(Node(B, C));
G[B].push_back(Node(A, C));
}
Prim();
return 0;
}
数论
Dead Fraction(POJ 1930)
题目链接:Dead Fraction
参考博文:poj 1930 Dead Fraction
参考博文:POJ - 1930 Dead Fraction(简单数学推理)
Prime Path(POJ 3126)
题目链接:Prime Path
- 题目大意:给你两个四位数m和n,求m变到n至少需要几步;每次只能从个十百千上改变一位数字,并且改变后的数要是素数。
- 思路:使用埃氏筛法来筛选出给定范围内的所有素数,然后使用BFS搜索到达n的最短路径,使用素数数组来作为判定减枝条件。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int MAX = 10000;
int isPrime[MAX];
int vis[MAX];
int T, from, to;
struct Node
{
int n, t;//n是数字,t是步数
Node(int n=-1, int t=-1): n(n), t(t){}
};
queue<Node> q;
void getPrime()
{
fill(isPrime, isPrime+MAX, 1);
isPrime[1] = 0;
for(int i=2; 2*i<MAX; i++)
{
if(isPrime[i])
{
for(int j=2*i; j<MAX; j+=i)
{
isPrime[j] = 0;
}
}
}
}
void bfs(int from)
{
while(!q.empty()) q.pop();
memset(vis, 0, sizeof(vis));
q.push(Node(from, 0));
vis[from] = 1;
while(!q.empty())
{
Node u = q.front(); q.pop();
if(u.n==to)
{
printf("%d\n", u.t);
return;
}
int k = 1;
for(int i=0; i<4; i++)
{
for(int j=0; j<10; j++)//注意从0开始
{
if(i==3 && j==0) continue;//首位不能是0
int v = u.n/(k*10)*(k*10)+(u.n%k)+j*k;//组合成新的数字
if(isPrime[v] && vis[v]==0)//判定减枝
{
q.push(Node(v, u.t+1));
vis[v] = 1;
}
}
k *= 10;
}
}
printf("Impossible\n");
}
int main()
{
scanf("%d", &T);
getPrime();
while(T--)
{
scanf("%d%d", &from, &to);
bfs(from);
}
return 0;
}
X-factor Chains(POJ 3421)
题目链接:X-factor Chains
参考博文:POJ 3421 X-factor Chains (因式分解+排列组合)
- 题目大意:一条整数链,要求相邻两数前一个整除后一个。给出链尾的数,求链的最大长度以及满足最大长度的不同链的数量。
- 思路:因式分解的素因子个数即为链长。因为链中后一个数等于前一个数乘以某素因子(相当于1乘以一些素数然后得到X),所以链的数量即为这些素因子不相异的全排列数:A!/(a1!a2!a3!..)
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 1<<20+1;
typedef long long LL;
LL X, p[MAX], a[MAX];
int main()
{
while(scanf("%lld", &X)!=EOF)
{
int cnt = 0;
//素因子分解(因为从小到大分解,得到的一定是素数)
for(LL i=2; i*i<=X; i++)
{
if(X%i==0)
{
p[cnt] = i;
a[cnt++] = 1;
X /= i;
}
while(X%i==0)
{
a[cnt-1]++;
X /= i;
}
}
if(X>1)//这一步容易遗忘
{
p[cnt] = X;
a[cnt++] = 1;
}
//相关计算
LL A = 0, ans = 1;
for(int i=0; i<cnt; i++)
{
A += a[i];
}
for(LL i=2; i<=A; i++)
{
ans *= i;
}
for(LL i=0; i<cnt; i++)
{
for(LL j=2; j<=a[i]; j++)
{
ans /= j;
}
}
printf("%lld %lld\n", A, ans);
}
return 0;
}
Semi-prime H-numbers(POJ 3292)
题目链接:Semi-prime H-numbers
参考博文:Semi-prime H-numbers POJ - 3292(简单打表)
- 题目大意:定义一种数叫H-numbers,它是所有能除以四余一的数。
在H-numbers中分三种数:
1、H-primes,这种数只能被1和它本身整除,不能被其他的H-number整除,例如9是一个H-number,能被1,3,9整除,但3不是H-number,所以他是H-primes。
2、H-semi-primes是由两个H-primes相乘得出的,H-semi-prime只能分解成两个H-prime数相乘。
3、剩下的是H-composite。
问给一个数,求1到这个数之间有多少个H-semi-primes。 - 思路:打表得出所有H-semi-primes,然后使用数组累积得到输出结果。我的方法有点麻烦,虽然思想是一样的,还是代码能力不行。
先看别人的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define MAXN ((int)1e6 + 10)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int vis[MAXN];
int ans[MAXN];
void prime_()
{
for(int i = 5; i <= MAXN; i += 4)
{
for(int j = 5; j <= MAXN; j += 4)
{
if(i * j > MAXN) break;//超出范围
//i 与 j 都不可以被分解,换句话说就是之前没有被构造出来过
//i*j一定符合4*n+1,这时的i*j定是H-semi—prime,以后再以i * j作为因子的数就不可以构成了,仔细体会下
if(!vis[i] && !vis[j]) vis[i * j] = 1;//得到H-semi—prime
//之前被构造出来过 所以 i * j 可以被分解成至少三个H-prime,所以不行
else vis[i * j] = -1;
}
}
int len = 0;//累积
for(int i = 1; i <= MAXN; i++){ //打表,计算出1-i之间的多少个H-semi—prime数
if(vis[i] == 1) len++;
vis[i] = len; //有点像前缀和
}
}
int main()
{
int n;
prime_();
while(~scanf("%d", &n) && n)
{
printf("%d %d\n", n, vis[n]);
}
}
自己的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAX = 1000002;
int h, flag[MAX], ans[MAX];
vector<int> v;
int r[MAX];
//打表+筛选
void isHPrime()
{
memset(flag, 1, sizeof(flag));
//得到H素数,存入数组,并将H-composite标记为0
for(int i=1; i<=MAX/4; i++)
{
int j = 4*i+1;
if(j>=MAX) break;
if(flag[j])
{
for(int k=2; k<MAX; k++)
{
if((k*j-1)%4!=0) continue;
int a = (k*j-1)/4;
if(4*a+1>=MAX) break;
flag[4*a+1] = 0;
}
v.push_back(j);
}
}
//标记所有的H_s_p
int k = 0;
r[k++] = 0;//作为一个初始数据方便下面的程序
for(int i=0; i<v.size(); i++)
{
for(int j=i; j<v.size(); j++)
{
if(MAX/v[i]<v[j]) break;//很重要,不然程序会溢出
LL u = v[i]*v[j];
if(u<MAX && flag[u]==0)//乘积是H-composite
{
r[k++] = u;
}
else
break;
}
}
sort(r, r+k);//排序,方便下面的程序
//更新数量数组
ans[0] = 0;
for(int i=1; i<k; i++)//r的下标
{
for(int j=r[i-1]+1; j<r[i]; j++)//相邻r元素之间的数据赋同样的值
{
ans[j] = ans[j-1];
}
ans[r[i]] = ans[r[i]-1]+1;//加一
}
}
int main()
{
isHPrime();
while(scanf("%d", &h)!=EOF && h)
{
printf("%d %d\n", h, ans[h]);
}
return 0;
}
Raising Modulo Numbers(POJ 1995)
- 题目大意:(A1^B1 +A2^B2+ … +AH^BH)mod M。
- 思路:快速幂乘,只不过每乘一次或加一次都需要模M一次。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
LL Z, H, M, A, B;
LL Pow(LL a, LL p)
{
LL ans = 1, t = p;
while(t)
{
if(t&1) ans = (a*ans)%M;
a = (a*a)%M;
t >>= 1;
}
return ans;
}
int main()
{
scanf("%lld", &Z);
while(Z--)
{
scanf("%lld%lld", &M, &H);
LL cnt = 0;
for(LL i=0; i<H; i++)
{
scanf("%lld%lld", &A, &B);
cnt = (cnt+Pow(A, B))%M;
}
printf("%lld\n", cnt);
}
return 0;
}
Pseudoprime numbers(POJ 3641)
题目链接:Pseudoprime numbers
- 题目大意:给出 p 和 a,若 a^p 和a对 p 同余且 p 不是素数,则输出 yes,否则输出 no。
- 思路:快速幂乘,且判断素数。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
LL p, a;
int IsPrime(LL p)
{
for(LL i=2; i*i<=p; i++)
{
if(p%i==0)
return 0;
}
return 1;
}
LL Pow(LL a, LL p)
{
LL ans = 1, t = p;
while(t)
{
if(t&1) ans = (a*ans)%p;
a = (a*a)%p;
t >>= 1;
}
return ans;
}
void solve(LL a, LL p)
{
if(IsPrime(p)||Pow(a, p)!=a%p)
printf("no\n");
else
printf("yes\n");
}
int main()
{
while(scanf("%lld%lld", &p, &a)!=EOF && (a+p))
{
solve(a, p);
}
return 0;
}