1、比赛(contest)
N(1 <= N <= 100)个同学按1..N依次编号参加羽毛球比赛。每人的实力不尽相同,不存在两个人实力相同的情况。
比赛分成若干轮进行,每一轮是两个人对决。如果编号为A的同学的实力强于编号为B的同学(1 <= A <= N; 1 <= B <= N; A != B),那么她们的对决中,编号为A的同学总是能胜出。
你为了知道实力的具体排名,于是你找来M(1 <= M <= 4,500)轮比赛的结果,希望能根据这些信息,推断出尽可能多的同学得实力排名。比赛结果保证不会自相矛盾。
输入格式:
第1行:用空格隔开的2个整数:N 和 M
第2..M+1行: 每行2个整数A、B(用空格隔开的),描述一轮比赛,每行的第一个编号为胜者
输入样例:
5 5
4 3
4 2
3 2
1 2
2 5
输出格式:
1个整数,表示排名可确定的同学的数目
输出样例:
2
输出说明:
编号为2的同学的排名为第4,编号为5的同学排名最后。其他3人的排名仍无法确定。
题解:
啊这个题一眼秒?top原图+逆图一波然后bitset乱搞?
代码:
#include <queue>
#include <bitset>
#include <cstdio>
#include <cstring>
#define N 105
#define M 4505
using namespace std;
int n,m,i;
bitset<105>in[N],out[N];bool vis[N];
int tot,nxt[M],point[N],v[M],tot1,nxt1[M],point1[N],v1[M],du[N],du1[N];
void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}
void addline1(int x,int y){++tot1; nxt1[tot1]=point1[x]; point1[x]=tot1; v1[tot1]=y;}
void top()
{
int i;
queue<int>q;
for (i=1;i<=n;i++)
{
if (!du[i]) q.push(i);
in[i][i]=1;
}
while (!q.empty())
{
int now=q.front(); q.pop();
vis[now]=1;
for (int i=point[now];i;i=nxt[i])
{
in[v[i]]|=in[now];
du[v[i]]--;
if (!du[v[i]] && !vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
memset(vis,0,sizeof(vis));
for (i=1;i<=n;i++)
{
if (!du1[i]) q.push(i);
out[i][i]=1;
}
while (!q.empty())
{
int now=q.front(); q.pop();
vis[now]=1;
for (int i=point1[now];i;i=nxt1[i])
{
out[v1[i]]|=out[now];
du1[v1[i]]--;
if (!du1[v1[i]] && !vis[v1[i]]) vis[v1[i]]=1,q.push(v1[i]);
}
}
}
int main()
{
freopen("contest.in","r",stdin);
freopen("contest.out","w",stdout);
scanf("%d%d",&n,&m);
for (i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addline(x,y);du[y]++;
addline1(y,x);du1[x]++;
}
top();
int ans=0;
for (i=1;i<=n;i++)
if (in[i].count()+out[i].count()-1==n) ans++;
printf("%d",ans);
}
2、运动计划(run)
地鼠误入一个隧道它必须在N(1 <= N <= 10,000)分钟内离开,否则隧道就会被人注满水,在每分钟的开始,地鼠会选择下一分钟是用来爬行还是休息。 地鼠的体力限制了她爬行的距离。更具体地,如果地鼠选择在第i分钟内爬行,她可以在这一分钟内爬可k_i(1 <= k_i <= 1,000)米,并且她的绝望指数会增加1。不过,无论何时地鼠的绝望指数都不能超过M(1 <= M <= 500)。如果地鼠选择休息,那么她的绝望指数就会每分钟减少1,但她必须休息到绝望指数恢复到0为止。在绝望指数为0时休息的话,绝望指数不会再变动。逃离开始时,地鼠的绝望指数为0。还有,在N分钟的逃离结束时,地鼠的绝望指数也必须恢复到0,否则她将对自己的一生失去信心。请你计算一下,地鼠最多能爬多少米。(已知地鼠爬行距离达到最大时恰好能逃离隧道。
输入格式:
第1行: 2个整数:N 和 M(用空格隔开)
第2。。N+1行: 第i+1行为1个整数:k_i
输入样例 (run.in):
5 2
5
3
4
2
10
输出格式:
输出一个整数,表示地鼠能爬行的最大距离
输出样例 (run.out):
9
输出说明:
第1分钟内爬行,在第2分钟内休息,在第3分钟内爬行,剩余时间用来休息。
题解:
以前好像做过这道题
第一次写挂了20pts?被学弟踩
尽量避免让状态向未来转移啊!
dp的状态很好设计:dp[i][j]表示时间i绝望值为j的最远距离
代码:
#include <cstdio>
#include <iostream>
using namespace std;
int f[10005][505],k[10005];
int main()
{
freopen("run.in","r",stdin);
freopen("run.out","w",stdout);
int n,m,i,j,l;
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++) scanf("%d",&k[i]);
for (i=1;i<=n;i++)
{
f[i][0]=max(f[i][0],f[i-1][0]);
for (j=m;j>=1;j--)
{
f[i][j]=max(f[i][j],f[i-1][j-1]+k[i]);
if (i>j) f[i][0]=max(f[i][0],f[i-j][j]);
}
}
printf("%d",f[n][0]);
}
3、找路径(road)
图上有N(1 <= N <= 1,000)个点,一共m(1 <= m<= 10,000)条边。 第i条边的端点为s_i、t_i,长度为L_i (1 <= L_i <= 1,000,000)。数据中保证每对{s_i,t_i}最多只出现1次。你的任务是找一条将点1和点N连起来的路径,如果存在不超过k(0=< k<=n)条边的路径则输出0。否则输出第k+1大的边的最小值.
输入格式:
第1行: 3个整数:N m K(用空格隔开)
第2..m+1行: 第i+1行为3个整数:s_i t_i L_i(用空格隔开)
输入样例
5 7 1
1 2 5
3 1 4
2 4 8
3 2 3
5 2 9
3 4 7
4 5 6
输出格式:
1个整数
输出样例
4
输出说明:
路径为:1->3->2->5
题解:
这个一开始以为是原题,只不过那个题让求最小距离,这个让求最大值的最小值
熟悉不?二分!
可以转化为最短路,比mid长的长度设为1,其他设为0,如果最短路小于等于k,说明这个值ok啊
题目没说双向边差评!题目不给不连通-1差评!
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define N 1005
#define M 30005
#define INF 1e9
using namespace std;
struct hh{int maxx,k;};
int tot,nxt[M],point[N],v[M],c[M],dis[N],k,n,m;
bool vis[N],vv=0;
void addline(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void spfa()
{
queue<int>q;
q.push(1);
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
while (!q.empty())
{
int now=q.front();q.pop();
for (int i=point[now];i;i=nxt[i])
if (dis[v[i]]>dis[now]+1)
{
dis[v[i]]=dis[now]+1;
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
if (dis[n]>INF) printf("-1"),vv=1;
else if (dis[n]<=k) printf("0"),vv=1;
}
bool check(int mid)
{
queue<int>q;
q.push(1);
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
while (!q.empty())
{
int now=q.front();q.pop();vis[now]=0;
for (int i=point[now];i;i=nxt[i])
if (c[i]<=mid)
{
if (dis[v[i]]>dis[now])
{
dis[v[i]]=dis[now];
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
else
if (dis[v[i]]>dis[now]+1)
{
dis[v[i]]=dis[now]+1;
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
if (dis[n]>k) return 0;
return 1;
}
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
int l=INF,r=0;
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addline(x,y,z);r=max(r,z);l=min(l,z);
}
spfa();
if (vv) return 0;
int ans=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
}
4、道路(roads)
有一棵树,有m(1到150)个节点,如果砍掉几条边这棵树就会变成多棵树,求最少砍掉几条边可以在得到的树中至少有一颗节点个数为n(1到m)。
输入说明:
第1行 m n , 第2到第m行,每行两个整数a 和b,表示a是b的父结点(1号点为根节点)。
输出说明:
一个数为最少砍掉的边数。
输入样例:
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
输出样例:
2
样例说明:
砍掉1—4和1—5。
题解:
好像是原题?
f[i][j]表示以i为根节点分离出来j个点的最少切割数量
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200
using namespace std;
const int INF=1e9;
int tot,nxt[N*2],point[N],v[N*2],size[N],f[N][N],n,ans,m,du[N];
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void treedp(int x,int fa)
{
f[x][1]=du[x];
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
treedp(v[i],x);
for (int j=m;j>=1;j--)
for (int k=1;k<=j;k++)
f[x][j]=min(f[x][j],f[v[i]][k]+f[x][j-k]-2);
}
ans=min(ans,f[x][m]);
}
int main()
{
freopen("roads.in","r",stdin);
freopen("roads.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addline(x,y);du[x]++;du[y]++;
}
memset(f,0x3f,sizeof(f));
ans=INF;
treedp(1,0);
printf("%d",ans);
}