版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/82530020
前言:比较OTL
JZOJ 3470 最短路
题目
给出一个 个节点, 条边的有向图,并且有 个点必须走过,问从起点 到终点 的最短路径
分析
跑 遍 ,在起点跑一次 ,然后剩下的其实就是深搜或者状压了(我用了状压),时间复杂度
代码
#include <cstdio>
#include <cstring>
#include <queue>
#define p 707406378
#define mmm memset(d,127/3,sizeof(d)),memset(v,0,sizeof(v))
typedef unsigned int uit;
struct node{uit y,w,next;}e[100001]; bool v[50001];
uit ls[50001],o[11],d[50001],dis[12][11],f[11][2048];
std::queue<uit>q; uit n,m,k,s,t,ans=4e9;
uit in(){
uit ans=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
void spfa(){//spfa
while (q.size()){
uit x=q.front(); q.pop();
for (register uit i=ls[x];i;i=e[i].next)
if (d[x]+e[i].w<d[e[i].y]){
d[e[i].y]=d[x]+e[i].w;
if (!v[e[i].y])
v[e[i].y]=1,q.push(e[i].y);
}
v[x]=0;
}
}
void hpfa(uit h){//预处理
d[o[h]]=0; q.push(o[h]); v[o[h]]=1; spfa();
for (register uit i=1;i<=k;i++) dis[i][h]=d[o[i]]; dis[11][h]=d[t];//终点标记
}
uit min(uit a,uit b){return a<b?a:b;}
void print(uit ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
int main(){
n=in(); m=in(); k=in(); s=in(); t=in();
for (register uit i=1;i<=m;i++){
register uit x=in();
e[i]=(node){in(),in(),ls[x]}; ls[x]=i;
}
for (register uit i=1;i<=k;i++) o[i]=in();
for (register uit i=1;i<=k;i++) mmm,hpfa(i);//k个标记点spfa
mmm,v[s]=1,d[s]=0,q.push(s),spfa();//从起点跑spfa
if (!k) if (d[t]==p) putchar('-'),putchar(49);
else print(d[t]);//没有标记点
else{
for (register uit i=1;i<(1<<k);i++)
for (register uit j=1;j<=k;j++){
f[j][i]=4e9;
if (i&(1<<j-1)){//存在相同的点
if (i^(1<<j-1)){//也存在不同的点
for (register uit q=1;q<=k;q++)
if (q!=j&&i&(1<<q-1))//找到中间点
f[j][i]=min(f[j][i],f[q][i^(1<<j-1)]+dis[j][q]);//dp
}else f[j][i]=d[o[j]];//那么直接赋为从起点到该标记点的最短路径
}
}
uit ans=4e9;
for (register uit i=1;i<=k;i++) ans=min(ans,f[i][(1<<k)-1]+dis[11][i]);//求最终答案(每个标记点到终点的距离+dp的结果)
if (ans==4e9) putchar('-'),putchar(49); else print(ans);//可能没有解
}
return 0;
}
JZOJ 3487 剑与魔法
题目
有一些中止条件,在中止条件之前的任务数量必须小于中止条件的限制,问最多能获得多少任务点数
分析
用贪心的思想,要大的总比要小的好,于是建小根堆,每次插入任务点数,当满足中止条件不断弹出小根堆的堆顶,最后求出小根堆任务点数总和即可,时间复杂度
代码
#include <cstdio>
#include <queue>
std::priority_queue<int>q;
int n,ans;
int in(){
int ans=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans;
}
int main(){
n=in();
for (register int i=1;i<=n;i++){
char c=getchar();
while (c<97||c>122) c=getchar();
int x=in();
if (c=='c') q.push(-x);//优先队列默认大根堆
else if (i<n) while (q.size()>=x) q.pop();//弹出
}
while (q.size()) ans-=q.top(),q.pop();//求出答案
return !printf("%d",ans);
}
JZOJ 3493 三角形
题目
在一个平面直角坐标系,有 个格点,问能组成多少个三角形
分析
伪代码
ans=n*(n-1)*(n-2)/6(组合C(n,3))//任取三个点的方案
for (register int i=1;i<n-1;i++)
for (register int j=i+1;j<n;j++)
for (register int k=j+1;k<=n;k++)
if ((x[i]-x[j])*(y[i]-y[k])==(x[i]-x[k])*(y[i]-y[j])) ans--;//移项
然后就可以拿到60分
当然正解是
,也就是枚举一个点,然后求出每个点到该点的斜率,再判断即可
代码
#include <cstdio>
#include <cmath>
#include <algorithm>
int n,x[3001],y[3001]; long long ans; double c[3001];
int in(){
int ans=0,f=1; char c=getchar();
while ((c<48||c>57)&&c!='-') c=getchar();
if (c=='-') c=getchar(),f=-f;
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=in(); ans=(long long)n*(n-1)*(n-2)/6;//总方案
for (register int i=1;i<=n;i++) x[i]=in(),y[i]=in();
for (register int i=1;i<n-1;i++){
int t=0,m=0;
for (register int j=i+1;j<=n;j++)
if (x[i]==x[j]) t++;//特判
else c[++m]=(double)(y[j]-y[i])/(x[j]-x[i]);//斜率
ans-=(long long)t*(t-1)>>1; //横着的特判
std::sort(c+1,c+1+m); t=0;//排序
for (register int j=1;j<=m;j++){
if (j>1&&c[j]==c[j-1]) t++; else t=1;
ans-=t-1;//减去三点在一直线的方案
}
}
printf("%lld",ans);
return 0;
}