2018九江市赛
市赛终于没爆掉了
T1
这不傻逼题吗
我们知道,若两个分数\frac{w_i}{c_i}>\frac{w_j}{c_j},那么就会有w_i*c_j>w_j*c_i
所以直接用这个来判定大小(考场上想都没想就直接这么写了,因为精度问题直接做除法不行)
然后就是瞎搞了(比较还是排序随便写啊)
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node{
long long w,c,id;
}lemon[100100];
int n,cnt=0,s;
long long read()
{
long long x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
bool cmp(node p,node q)
{
return p.w*q.c>p.c*q.w;
}
int main()
{
freopen("market.in","r",stdin);
freopen("market.out","w",stdout);
scanf("%d%d",&n,&s);
int i;
for (i=1;i<=n;i++)
{
long long a,b;
a=read();b=read();
if (b<=s)
{lemon[++cnt].w=a;lemon[cnt].c=b;lemon[cnt].id=i;}
}
sort(lemon+1,lemon+1+cnt,cmp);
printf("%I64d",lemon[1].id);
return 0;
}
/*
4
4 15
4 10
8 10
10000 20
*/
T2
暴力是很显然的,无论是直接做还是拓扑排序
然后就在考场上推了1h怎么O(n)建图,我还是太菜了
数据范围告诉你肯定是O(n)或O(nlogn)
一个很明显的结论:如果d_i>d_j,那么无论r_i与r_j的大小,武器i的分组一定不会在武器j的前面
那么我们可以按照d从大到小排序
然后我们记一个best_i表示第i组中最大的d与r,这显然是一个递减序排列的数组
很明显如果一个武器的d(或r)大于best_i中的对应元素,那么这个武器的分组一定大于等于i
那么对于每一组新来的d与r,我们在best中进行二分查找找出它的位置,分别记为pos_d与pos_r
然后当前武器的分组就可表示为min(pos_d,pos_r),同时更新当前分组的best
这题就做完了,下面程序二分写得很丑请见谅
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node{
int d,r,id;
}wea[100100],best[100100];
int n,ans[100100],all=0;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
bool cmp(node p,node q)
{
return p.d>q.d;
}
int find(int x)
{
int pos1,pos2,l,r;
if (all==0) return 1;
if (best[1].d<wea[x].d) pos1=1;
else if (best[all].d>wea[x].d) pos1=all+1;
else
{
l=1,r=all;
while (l<=r)
{
int mid=(l+r)>>1;
if (best[mid].d<wea[x].d) {pos1=mid;r=mid-1;}
else l=mid+1;
//cout << "l=" << l << " r=" << r << endl;
}
}
if (best[1].r<wea[x].r) pos2=1;
else if (best[all].r>wea[x].r) pos2=all+1;
else
{
l=1,r=all;
while (l<=r)
{
int mid=(l+r)>>1;
if (best[mid].r<wea[x].r) {pos2=mid;r=mid-1;}
else l=mid+1;
//cout << "l=" << l << " r=" << r << "pos=" << pos2 <<endl;
}
//cout << endl;
}
//cout << pos1 << " " << pos2 << endl;
return min(pos1,pos2);
}
void out()
{
cout << all << endl;
for (int i=1;i<=all;i++) cout << best[i].d << " " << best[i].r << endl;
cout << endl;
}
int main()
{
freopen("tank.in","r",stdin);
freopen("tank.out","w",stdout);
n=read();
int i;
for (i=1;i<=n;i++) {wea[i].d=read();wea[i].r=read();wea[i].id=i;}
sort(wea+1,wea+1+n,cmp);
memset(ans,0,sizeof(ans));
memset(best,0,sizeof(best));
//for (i=1;i<=n;i++) cout << wea[i].d << " " << wea[i].r << " " << wea[i].id << endl;
for (i=1;i<=n;i++)
{
int pos=find(i);
ans[wea[i].id]=pos;
if (all<pos) all++;
best[pos].d=max(best[pos].d,wea[i].d);
best[pos].r=max(best[pos].r,wea[i].r);
//out();
}
for (i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
/*
5
1 4
2 2
3 3
4 1
5 5
*/
T3
考试的时候很简单的想了下这应该是一个树形dp,然后就果断弃疗了
由于题面很长,我们会引用题面的几部分来简化题意
周克华深知多走一分钟路就多一分钟暴露的危险,而且他之前已经完全摸清了辖区的地形,因此他总是走最短路,也就是,他访问任何一个结点时,走的路线都是从银行到这里的最短路。为了简化题目,我们保证从银行(结点1)到任何一个结点的最短路都是唯一的。
这一段是在告诉你:罪犯会在以1号节点为根节点的最短路树上运动
只要有相邻的结点能满足“不走回头路、只走最短路”的前提,他一定会移动。如果有多个相邻结点可供选择,他会随机等概率选择一个作为他的移动目标。如果没有结点满足这一要求,那么周克华就会选择遁入深山之中,而可以想象在距离案发现场十万八千里的山区里抓捕周克华的难度,所以一旦周克华遁入山中,也就意味着Lemon的抓捕行动失败了。
这一段话在告诉你:罪犯会一直从深度小的地方向深度大的地方走,走到叶子结点时结束
有了上面两句话,我们可以开始考虑正解
首先把最短路树建出来,然后就在这棵树上跑dp,在这里的dp用bottom-up更为方便
记录dp[i][j]表示在以i为根节点的子树(包括i)上布置j名警察抓住罪犯的概率,那么最终答案存储在dp[1][s]中
dp[i][j]可以由两部分转移而来,一部分是在i节点上抓住罪犯,另一部分是在i节点上无法抓住罪犯,但是在i的子树上抓住了罪犯
很明显前一部分是数据已经给出,关键是后一部分
我们记一个辅助数组f[i]表示在子树上布置i名警察能抓住罪犯的概率,由于子树的信息全部已知,我们可以直接对当前节点的所有儿子节点做树上背包,最后乘上走到这一个儿子节点的概率即可
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node{
int to,nxt,cost;
}sq1[40010],sq2[40010];
int n,s,m,all1=0,all2=0,dis[210],head1[210],head2[210],son[210],pre[210];
double p[210][210],dp[210][210],f[210];
bool vis[210];
queue<int> q;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void add1(int u,int v,int w)
{
all1++;sq1[all1].to=v;sq1[all1].nxt=head1[u];sq1[all1].cost=w;head1[u]=all1;
}
void add2(int u,int v,int w)
{
all2++;sq2[all2].to=v;sq2[all2].nxt=head2[u];sq2[all2].cost=w;head2[u]=all2;
}
void init()
{
n=read();m=read();
memset(head1,0,sizeof(head1));
memset(head2,0,sizeof(head2));
int i,j;
for (i=1;i<=m;i++)
{
int u,v,w;
u=read();v=read();w=read();
add1(u,v,w);add1(v,u,w);
}
s=read();
for (i=1;i<=n;i++)
for (j=1;j<=s;j++) scanf("%lf",&p[i][j]);
}
void spfa()
{
memset(dis,0x3f,sizeof(dis));dis[1]=0;
memset(vis,0,sizeof(vis));q.push(1);
vis[1]=1;
while (!q.empty())
{
int u=q.front(),i;q.pop();vis[u]=0;
for (i=head1[u];i;i=sq1[i].nxt)
{
int v=sq1[i].to;
if (dis[u]+sq1[i].cost<dis[v])
{
dis[v]=dis[u]+sq1[i].cost;pre[v]=u;
if (!vis[v]) {vis[v]=1;q.push(v);}
}
}
}
}
void make_sq()
{
spfa();
int i,j;
for (i=2;i<=n;i++) add2(pre[i],i,1);
memset(son,0,sizeof(son));
for (i=1;i<=n;i++)
for (j=head2[i];j;j=sq2[j].nxt) son[i]++;
//for (i=1;i<=n;i++) cout << son[i] << " ";cout << endl;
}
/*f[i]记录在以当前节点为根节点时在它的子树时布置i个警察抓住罪犯
dp[i][j]记录在以i为根节点的子树(包括自己)抓住罪犯的概率
dp[i][j]包括两部分,一部分是在根节点i抓住罪犯,另一部分是罪犯在根节点i时逃脱,但是在子树上被抓住*/
void dfs(int u)
{
int i,j,k;
for (i=1;i<=s;i++) dp[u][i]=p[u][i];
if (!head2[u]) return;//叶子结点只可能在当前节点抓住罪犯
for (i=head2[u];i;i=sq2[i].nxt) dfs(sq2[i].to);//bottom-up dp
memset(f,0.0,sizeof(f));
for (i=head2[u];i;i=sq2[i].nxt)
{
for (j=s;j>=0;j--)
{
for (k=0;k<=j;k++)
f[j]=max(f[j],f[j-k]+dp[sq2[i].to][k]);//对所有儿子节点进行背包
}
}
for (i=0;i<=s;i++) f[i]=f[i]/son[u];//乘上每个点可能被走到的概率
for (i=0;i<=s;i++)
{
for (j=0;i+j<=s;j++)
{
dp[u][i+j]=max(dp[u][i+j],p[u][i]+1.0*f[j]*(1-p[u][i]));
}
}
}
void work()
{
dfs(1);
printf("%.4lf",dp[1][s]);
}
int main()
{
freopen("catch.in","r",stdin);
freopen("catch.out","w",stdout);
init();
make_sq();
work();
return 0;
}
/*
4 4
1 2 1
1 3 2
2 4 3
3 4 1
2
0.01 0.1
0.5 0.8
0.5 0.8
0.7 0.9
*/
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步