测验都是模板题;
A题:http://poj.org/problem?id=1258
题意:输入n,接下来有N*N的矩阵,代表从每个点到其他点的路径长度;要求每个点都要走到,求最短的长度是多少;明显的最小生成树模板题;
ps:prime算法:从源点开始,从集合中选取到其他点最短的点,再加入集合,然后更新距离,更新点;
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stdio.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=105;
int vis[N];
int dis[N];
int ma[N][N];
int n,ans;
void init(){//初始化
ans=0;
for(int i=1;i<=n;i++){
vis[i]=0;
for(int j=1;j<=n;j++){
ma[i][j]=inf;
}
}
}
void prime(){
for(int i=1;i<=n;i++)dis[i]=ma[1][i];
dis[1]=0; //从点1出发,
vis[1]=1; //将点1加入集合
for(int v=1;v<n;v++){ //将剩下的n-1个点加入集合,则代表所有点都被选取(也就是都能经过)的最短距离
int minn=inf;
int flag=0;
for(int i=1;i<=n;i++){
if(!vis[i]&&dis[i]<minn){
minn=dis[i];
flag=i;
}
}
vis[flag]=1; //遍历所有点,找到从集合中到未加入集合中的最短边的点;
ans=ans+dis[flag]; //加上距离
for(int i=1;i<=n;i++){ //更新
if(!vis[i]&&dis[i]>ma[flag][i]){
dis[i]=ma[flag][i];
}
}
}
}
int main(){
while(scanf("%d",&n)!=EOF){
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cin>>ma[i][j];
}
prime();
printf("%d\n",ans);
}
return 0;
}
Kruskal算法:找边,至始至终找最短的边,判断是否成环,即可,成环判断(用并查集)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 105;
struct Edge{
int u,v;
int w;
}edge[N*N];
int p[N],n,ans=0,m;
int maze[N][N];
void init(){
ans=0,m=0;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++)
maze[i][j]=inf;
}
}
int Find(int x){ //并查集压缩路径
return x == p[x] ? x : p[x] = Find(p[x]);
}
bool cmp(Edge a,Edge b){
return a.w<b.w;
}
void kruskal(){
sort(edge,edge+m,cmp); //将边的长短排序;
for(int i=0;i<=n;i++)p[i]=i; //初始化各个集合,将各个集合的爸爸设为自己,也就是一个人一个集合;
for(int i=0;i<m;i++){ //遍历所有的边,
int u=Find(edge[i].u);
int v=Find(edge[i].v);
if(u!=v){ //当不是一个集合时,代表不成环,将其加入
p[u]=v; //实际上可以用一个变量记录加入边的数量,如果 数量等于n-1则代表已经遍历所有的点
ans+=edge[i].w;
}
//例如 if(num==n-1)break;
}
}
int main(){
while(cin>>n){
init();
//ans=0,m=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cin>>maze[i][j];
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
edge[m].u=i;
edge[m].v=j;
edge[m++].w=maze[i][j];
}
}
kruskal();
cout<<ans<<endl;
}
return 0;
}
B题:http://poj.org/problem?id=3468
线段树模板题,题意:Q查询,C更改都是区间;
虽说是模板题,还是讲讲,懒标记:该标记的意思是如果有区间要更改值,我就先只更改自己,若某个操作要查询或者更改到自己的儿子,我就将懒标记传递下去,传递过程:则是自己区间长短*每个人要更改的值,因为区间更改是每个人要更改同样的数字,即(r-l+1)*f(f为懒标记也就是存的c 操作最后一个数的值);
#include <iostream>
using namespace std;
typedef long long int ll;
const ll N = 1e6+10;
struct Tree{ //l代表左边界,r为右边界,f为懒标记;
ll l,r,f;
ll w; //w为该区间的值;
}tree[N*4];
ll ans=0;
void down(ll node){
tree[node*2].f+=tree[node].f;
tree[node*2].w+=(tree[node*2].r-tree[node*2].l+1)*tree[node].f;
tree[node*2+1].f+=tree[node].f;
tree[node*2+1].w+=(tree[node*2+1].r-tree[node*2+1].l+1)*tree[node].f;
tree[node].f=0;
}
void build(ll node,ll a,ll b){
tree[node].l=a,tree[node].r=b;
if(tree[node].l==tree[node].r){//当递归到叶子时,输入值
cin>>tree[node].w;
return ;
}
ll m=(a+b)/2;
build(node*2,a,m);
build(node*2+1,m+1,b);
tree[node].w=tree[node*2].w+tree[node*2+1].w;//否则爸爸的值等于两个儿子的值相加
}
void querry(ll node,ll a,ll b){ //查询操作
if(tree[node].l>=a&&tree[node].r<=b){ //如果查询的区间大于 该区间,例如我查询2-5,该节点表示3-4,当然可以直接累加上
ans+=tree[node].w;
return ;
}
if(tree[node].f)down(node); //如果有懒标记,则下传
ll m=(tree[node].l+tree[node].r)/2;
if(a<=m)querry(node*2,a,b);
if(b>m)querry(node*2+1,a,b);
}
void add(ll node,ll a,ll b,ll v){ //区间修改
if(tree[node].l>=a&&tree[node].r<=b){
tree[node].w+=(tree[node].r-tree[node].l+1)*v;
tree[node].f+=v; //标记上懒标记
return ;
}
if(tree[node].f)down(node);
ll m=(tree[node].l+tree[node].r)/2;
if(a<=m)add(node*2,a,b,v);
if(b>m)add(node*2+1,a,b,v);
tree[node].w=tree[node*2].w+tree[node*2+1].w;
}
int main()
{
ll n,q;
cin>>n>>q;
build(1,1,n); //建树
// for(ll i=1;i<=15;i++)
// cout << tree[i].w<< endl;
while(q--){
ans=0;
char type;
cin>>type;
if(type=='Q'){
ll x,y;
cin>>x>>y;
querry(1,x,y);
printf("%I64d\n",ans);
}
else{
ll x,y,z;
cin>>x>>y>>z;
add(1,x,y,z);
}
}
return 0;
}
C题:http://poj.org/problem?id=1064
有n根缆线,分成K段,使每段最长,求长为多少;
ps:利用二分;
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stdio.h>
typedef long long int ll;
using namespace std;
const int N = 100010;
ll a[N];
int n,k;
bool binary(ll t){
ll res=0;
for(int i=0;i<n;i++){
res+=(ll)(a[i]/t);
}
if(res>=k)return true;
else
return false;
}
int main(){
while(~scanf("%d%d",&n,&k)){
ll maxx=-999;
for(int i=0;i<n;i++){
double qq;
cin>>qq;
a[i]=qq*100.0; //划为整数
maxx=max(maxx,a[i]);
}
ll l=1,r=maxx,lon=0;
//int number=100;
while(l<=r){
ll mid=(l+r)/2;
if(binary(mid)){
lon=max(lon,mid);
l=mid+1;
}
else
r=mid-1;
}
printf("%.2f\n",double(lon/100.0));
//cout<<(lon/100.0)<<endl;
}
return 0;
}
D题:http://poj.org/problem?id=3061
给你一个数,代表有几个测试样例,接下来n,k,代表n个数,连续元素相加大于k的最小序列长度;
可用前缀和二分找长度,也可以用尺取法,(尺取法O(n)好点)
#include <iostream>
using namespace std;
const int N = 1e5+10;
int a[N];
int main()
{
int _;
cin>>_;
while(_--){
int left=1,right=1,sum=0; //left代表 左边界,right右边界;
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++)cin>>a[i];
int ans=n+1; //区间长度
sum=a[1];
while(right<n){
if(sum<s&&ans>(right-left+1)){ //如果接下来的区间长度已经大于ans了,就没必要再扩展了,直接去掉左区间的值即可
right++;
sum+=a[right];
}
else{
ans=min(ans,right-left+1);
sum-=a[left];
left++;
}
}
if(ans<=n)
cout << ans << endl;
else
cout<<'0'<<endl;
}
return 0;
}
G题:http://poj.org/problem?id=3255
次短路径:在堆优化的dijkstra上加一个 次短路dis2;
#include <iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N = 100010;
#define inf 0x3f3f3f3f
struct Edge{
int to;
int cost;
Edge(int tt=0,int cc=0):to(tt),cost(cc){}
};
int dis[5005],dis1[5005];
typedef pair<int,int> p;
int n,r;
vector<Edge>edge[N];
void dijkstra(){
fill(dis,dis+n+1,inf);//将dis,dis1初始为inf
fill(dis1,dis1+n+1,inf);
dis[1]=0;
priority_queue<p,vector<p>,greater<p> > q;
q.push(p(0,1));
while(!q.empty()){
p now=q.top();
q.pop();
int v=now.second,d=now.first;
if(d>dis1[v])continue;
for(int i=0;i<edge[v].size();i++){
Edge e=edge[v][i];
int d2=e.cost+d;
if(dis[e.to]>d2){
swap(dis[e.to],d2);
q.push(p(dis[e.to],e.to));
}
if(d2>dis[e.to]&&d2<dis1[e.to]){//更新次短路径
dis1[e.to]=d2;
q.push(p(dis1[e.to],e.to));
}
}
}
cout<<dis1[n]<<endl;
}
int main()
{
cin>>n>>r;
for(int i=1;i<=r;i++){
int u,v,w;
cin>>u>>v>>w;
edge[u].push_back(Edge(v,w));
edge[v].push_back(Edge(u,w));
}
dijkstra();
//cout << "Hello world!" << endl;
return 0;
}
H题:http://acm.hdu.edu.cn/showproblem.php?pid=1237
简单计算器;
#include<iostream>
#include<stack>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<iomanip>
#include<string>
using namespace std;
int main(){
char op;
double num,temp;
while(scanf("%lf",&num)){
stack<double>sum;
op=getchar();
if(num==0&&op=='\n')break;
sum.push(num);
while(1){
scanf("%c %lf",&op,&num);
switch(op){
case '+':sum.push(num);break;
case '-':sum.push(-num);break;
case '*':{temp=sum.top();sum.pop();sum.push(temp*num);break;}
case '/':{temp=sum.top();sum.pop();sum.push(temp/num);break;}
}
if(getchar() == '\n')break;
}
double ans=0;
while(!sum.empty()){
ans+=sum.top();
sum.pop();
}
printf("%.2f\n",ans);
}
return 0;
}
H题:http://acm.hdu.edu.cn/showproblem.php?pid=1896
题意:丢石子,奇数丢,偶数不丢,求最后最远的石子离刚开始的石子多远;
ps:利用优先队列,先按距离排序,距离相同的丢得近的先 出队,利用temp 每次 !temp,则代表 奇偶;
#include <iostream>
#include<queue>
using namespace std;
struct stone{
int pi;
int di;
bool operator < (const stone &u) const {
if(pi==u.pi){
return di>u.di;
}
return pi>u.pi;
}
};
int main()
{
int _;
cin>>_;
while(_--){
stone p;
priority_queue<stone> q;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>p.pi>>p.di;
q.push(p);
}
bool temp=true;
int ans=0;
while(!q.empty()){
stone e;
e=q.top();
q.pop();
if(temp){
e.pi+=e.di; //距离 加上丢的距离,再入队
q.push(e);
}
ans=e.pi;
temp=!temp; //变奇偶
}
cout << ans<< endl;
}
return 0;
}