2019牛客暑假团队赛第八场Ak(POJ3569-3669经典试题总结)

A、Cell Phone Network

POJ3659
题目描述
中文题意:
农夫约翰决定给他的每头奶牛打个电话,希望能鼓励他们的社交互动。然而,这要求他在他的N(1≤N≤10000)牧场上设置手机信号塔(方便编号为1…N),这样他们就可以进行通信。
恰好N-1对牧场是相邻的,对于任何两个牧场A和B(1≤A≤N;1≤B≤N; A≠B),有一系列相邻的牧场,使得A是第一个牧场。序列和B是最后一个。农夫约翰只能将手机塔放置在牧场中,每个塔都有足够的范围来为牧场提供服务,并且所有牧场都与牧场相邻。
帮助他确定他必须安装的塔的最小数量,以便为每个牧场提供手机服务。
输入
第一行正数N 接下来2-N行,每行一个A和B
输出
最少安装的塔数
思路:
本来想着可以把这个图看成一个二分图,求最大的匹配进而得到最小点覆盖,无限Wa之后发现题意没理解清楚,不应该这样写1-N个节点 例子:1-2-3-4-5-6 (直线联通)这种情况根据二分图写的话需要3个塔而按照题意两个塔就够了分别放在2和5上面。所以问题又转化成了求图的最小支配集或者用树形DP写,相当于给你一棵树,当你给一个节点染色时,他的相邻节点都会被染上色,问最少选择多少节点染色,可以将整棵树染上。这是树的最小支配集模板题,贪心做的。
代码

/*
思路 
树的最小支配集 先用dfs搜一遍这个数,然后把每个点的father确定下来,
我理解的dfs就是用来初始化father数组的 然后再利用father存放的信息
利用反向序列倒着进行贪心,开两个集合,一个S[]指的是已经被覆盖的点
另外一个Set[]指的是支配集中的点 
对于一个即不属于支配集又不属于与支配集中的点相连的点来说,如果他的父亲
节点不属于支配集,就把父节点加入进来这就是贪心思路,从后向前找,看他父亲有没有被覆盖,
这样就可以将与它父亲相连的和它自己都标记点 从而实现最小支配集
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
const int INF=0x3f3f3f3;
typedef long long ll; 
struct Edge
{
	int to;
	int next;
}Edges[maxn];
int head[maxn],father[maxn],NewPos[maxn];
bool vis[maxn];
int n,m,now;
//vis用于dfs标记
// father[]记录节点的父亲节点
//now 表示当前dfs序列中已经有的点个数
//Newpos[]表示dfs序列中第i个点是哪个店 
int cnt;
//邻接表存图 
void add(int u,int v){
	Edges[cnt].to=v;
	Edges[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int x) {
	NewPos[now++]=x;
	for(int k=head[x];k!=-1;k=Edges[k].next) {
		if(!vis[Edges[k].to]) {
			vis[Edges[k].to]=true;
			father[Edges[k].to]=x;
			dfs(Edges[k].to);
		}
	}
}
bool S[maxn],Set[maxn];
// 贪心求树的最小支配集 
int Greedy()
{
	memset(S,0,sizeof(S));
	memset(Set,0,sizeof(Set));
	int ans=0;
	for(int i=n-1;i>=0;i--) {//序列反向
		int t=NewPos[i];
		if(!S[t]) {//当前点未被覆盖 即不属于支配集也不与支配集中的点连接 
			if(!Set[father[t]]) {//父节点不属于支配集 加入进来 
				Set[father[t]]=true;
				ans++;
			}
			//与父亲节点相连的点都被标记掉 
			S[t]=true;
			S[father[t]]=true;
			S[father[father[t]]]=true;
		}
	}
	return ans;
}
/*
//贪心求树的最小覆盖集
int Greedy2()
{
	memset(S,0,sizeof(S));
	memset(Set,0,sizeof(Set));
	int ans=0;
	for(int i=n-1;i>=0;i--) {
		int t=NewPos[i];
		if(!S[t]&&!S[father[t]]) {
			Set[father[t]]=true;
			ans++;
			S[t]=true;
			S[father[t]]=1;
		}
	}
	return ans;
 } 
// 贪心求树的最大独立集 
 int Greedy3() {
 	memset(S,0,sizeof(S));
 	memset(Set,0,sizeof(Set));
 	int ans=0;
 	for(int i=n-1;i>=0;i--) {
 		int t=NewPos[i];
 		if(!S[t]) {
 			Set[t]=1;
 			ans++;
 			S[t]=1;
 			S[father[t]]=1;
		 }
	 }
	 return ans;
 } */
int Xiaobo()
{
	int u,v;
	cin>>n;
	memset(father,0,sizeof(father));
	memset(NewPos,0,sizeof(NewPos));
	memset(vis,false,sizeof(vis));
	memset(Edges,0,sizeof(Edges));
	memset(head,-1,sizeof(head));
	for(int i=0;i<n-1;i++) {
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	now=0;
	vis[1]=true;
	father[1]=1;
	dfs(1);
	cout<<Greedy()<<endl;
	return 0;
}

B、iCow

题目描述
Fatigued by the endless toils of farming, Farmer John has decided
to try his hand in the MP3 player market with the new iCow. It is
an MP3 player that stores N songs (1 <= N <= 1,000) indexed 1 through
N that plays songs in a “shuffled” order, as determined by Farmer
John’s own algorithm:

  • Each song i has an initial rating Ri (1 <= Ri <= 10,000).
  • The next song to be played is always the one with the highest rating (or, if two or more are tied, the highest rated song with the lowest index is chosen).
  • After being played, a song’s rating is set to zero, and its rating points are distributed evenly among the other N-1 songs.
  • If the rating points cannot be distributed evenly (i.e., they are not divisible by N-1), then the extra points are parceled out one at a time to the first songs on the list (i.e., R1, R2, etc. – but not the played song) until no more extra points remain.
    This process is repeated with the new ratings after the next song is played.
    Determine the first T songs (1 <= T <= 1000) that are played by the iCow.
    输入描述:
  • Line 1: Two space-separated integers: N and T
  • Lines 2…N+1: Line i+1 contains a single integer: Ri
    输出描述:
  • Lines 1…T: Line i contains a single integer that is the i-th song that the iCow plays.
    思路:水题,直接模拟,注意如果当前歌曲无法整除n-1的时候它的余数要依次向下分的,一首歌曲分一个,因为这个Wa了一发好亏~~~
    代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
int num[maxn];
int Xiaobo()
{
	int n,t;
	cin>>n>>t;
	for(int i=0;i<n;i++) {
		cin>>num[i];
	}
	while(t--) {
		if(n==1) {
			cout<<1<<endl;
			continue;
		}
		int mx=num[0],ind=0;
		for(int i=1;i<n;i++) {
			if(num[i]>mx) {
				mx=num[i];
				ind=i;
			}
		}
		cout<<ind+1<<endl;
		int yu=mx%(n-1);
		int chu=mx/(n-1);
        for(int i=0;i<n&&yu;i++) {
            if(i!=ind) {
                num[i]++;
                yu--;
            }
        }
		for(int i=0;i<n;i++) {
			if(i!=ind) num[i]+=chu;
			else {
				num[i]=0;
			}
		}
	}
}

C、阶乘之和

用高精度计算出S=1!+2!+3!+…+n!(n ≤ 50)其中“!”表示阶乘,例如:5!=54321。
输入
N
输出
阶乘之和Sum
思路:
高精度!!! 虽然说N的范围只有50,我也很奇怪为何double没爆过去,只能老老实实用数组模拟了
代码

#include<bits/stdc++.h>
int a[40002]={0,1},s[40002]={0};
int main()
{
	int i,n,alen=1,j,slen=1,k,l=1,v=1;
	scanf("%d",&n);
	for(k=1;k<=n;k++)
	{
		for(j=1;j<=alen;j++)
			a[j]*=l;
		for(j=1;j<=alen;j++)
		{	
			if(a[j]>=10)
			{
				a[j+1]+=a[j]/10;
				a[j]%=10;
				v++;
			}
		}	
		alen++;
		while(a[alen]>=10)
		{
			a[alen+1]=a[alen]/10;
			a[alen]%=10;
			alen++;
			v++;
		}
		l++;
		slen=slen>alen?slen:alen;
		for(i=1;i<=slen;i++)
		{
			s[i]+=a[i];
			if(s[i]>=10)
			{
				if(i==slen)
					slen++;
				s[i]%=10;
				s[i+1]++;
			}
		}
		alen=v;
	}
	for(i=slen;i>=1;i--)
	{
		if(s[i]==0&&slen>1)
			slen--;
		else
			break;
	}
	for(i=slen;i>=1;i--) printf("%d",s[i]);
	return 0;
}	

D、Artificial Lake

题面太长了还是直接点链接看吧
题目大意 :在这里插入图片描述
看了上图就知道了,我理解的就是假设从第一个开始浇水,然后水会依照平台的高低进行流,然后就是覆盖一个平台的含义就是覆盖它的宽度就好了 看上图第一个时间就是4这也是样例,所以如果纯模拟的话望一眼数据就知道必然超时了,所以我们可以想一下,无论从哪个平台可是浇水,它肯定会流向比它低中的最低的平台,那我们不如就从最低的开始浇水,让他先覆盖自己,然后再覆盖比它高的,具体实现就是先记录每个平台的高度与宽度以及他们的左、右平台,找到最低的平台,开始灌水。灌满最低平台后,相当于该平台消失,寻找下一流向哪个平台,再更新左右平台,直到最高的平台被水覆盖。
代码加注释

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=0x3f3f3f;
const int mod=1e9+7;
struct st
{
	ll width;//宽度
	ll height;//高度
	int left;//左边区间
	int right;//右边区间
}num[maxn];
int n,k;
ll ans[maxn];//记录当前高度的平台需要的时间
int Xiaobo()
{
 	cin>>n;
 	for(int i=1;i<=n;i++) {
 		cin>>num[i].width>>num[i].height;
	}
	for(int i=0;i<=n+1;i++) {//初始化每个平台的左边和右边
		num[i].left=i-1;
		num[i].right=i+1;
	}
	num[0].height=num[n+1].height=INF;//初始化高度为最大
	num[0].width=num[n+1].width=1;//宽度初始化为1
	int platform=0,mi=INF; //platform代表当前的平台,mi是用来找最小的平台
	for(int i=1;i<=n;i++) {
		if(num[i].height<mi) {
			mi=num[i].height;
			platform=i;
		}
	}
	ll cnt=1;
	ll sum=0;
	int left,right;
	while(cnt<=n) {
		cnt++;
		//platform代表的就是当前未覆盖平台中的最小平台
		sum+=num[platform].width;//记录其宽度,就是所用时间
		ans[platform]=sum;
		left=num[platform].left;
		right=num[platform].right;
		//用面积更新  就是灌满这个间隔的时间
		//num[platform].height-1是因为初始化就是1;
		sum+=(min(num[left].height,num[right].height)-num[platform].height-1)*num[platform].width;
		num[left].right=right;
		num[right].left=left;
		//平台platform被覆盖之后更新它左边的和它右边的平台 并且注意 它的宽度是并到与它挨着的相对小的平台
		//至于为何并在一起,是为了以后浇水计算面积及时间的时候用到的
		if(num[left].height<num[right].height) {
			num[left].width+=num[platform].width;
			platform=left;
		}
		else {
			num[right].width+=num[platform].width;
			platform=right;
		}
		//在左右的两边中的平台中找出最小的 就是想更新platform
		while(1) {
			left=num[platform].left;
			right=num[platform].right;
			if(num[left].height<num[platform].height) {
				platform=left;
			}
			else if(num[right].height<num[platform].height){
				platform=right;
			}
			else break;
		}
	}
	for(int i=1;i<=n;i++ ){
		printf("%lld\n",ans[i]);
	}
}

E、Haybale Guessing

POJ3657
题目描述
The cows, who always have an inferiority complex about their intelligence, have a new guessing game to sharpen their brains.
A designated ‘Hay Cow’ hides behind the barn and creates N (1 ≤ N ≤ 1,000,000) uniquely-sized stacks (conveniently numbered 1…N) of hay bales, each with 1…1,000,000,000 bales of hay.
The other cows then ask the Hay Cow a series of Q (1 ≤ Q ≤ 25,000) questions about the the stacks, all having the same form:
What is the smallest number of bales of any stack in the range of stack numbers Ql…Qh (1 ≤ Ql ≤ N; Ql ≤ Qh ≤ N)?The Hay Cow answers each of these queries with a single integer A whose truthfulness is not guaranteed.
Help the other cows determine if the answers given by the Hay Cow are self-consistent or if certain answers contradict others.
输入描述:

  • Line 1: Two space-separated integers: N and Q
  • Lines 2…Q+1: Each line contains three space-separated integers that represent a single query and its reply: Ql, Qh, and A
    输出描述:
  • Line 1: Print the single integer 0 if there are no inconsistencies among the replies (i.e., if there exists a valid realization of the hay stacks that agrees with all Q queries). Otherwise, print the index from 1…Q of the earliest query whose answer is inconsistent with the answers to the queries before it.

思路:
二分答案+并查集+区间染色
数轴上有n个点,没个点上的值都不同。然后给你Q次询问和答案,输入l,r,x,表示在区间[l,r]最小值为x,然后问你最早在哪个地方出现矛盾。 区间染色问题,可以用并查集来做。先二分出现矛盾的地方p,然后将1~p的询问值按大到小排序,若对于最小值相同的区间中出现不相交的两个区间,那么矛盾出现。 那么合法的情况就是这些最小值相同的区间必然是两两相交的,那么记录最小的左端点L和最大的右端点R,然后将[L,R]与L-1合并。所以在判断的时候还有判一下 当前最小值相同的区间的交集是否之前出现过,若出现过,则矛盾。

/*
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e6+5;
const int mod=1e9+7;
struct node
{
	int l,r,num;
}num[maxn];
node tmp[maxn];
int n,q,father[maxn];
int find(int x) {
	while(x!=father[x]) {
		x=father[x];
	}
	return x;
}
bool cmp(node a,node b) {
	return a.num>b.num;
}
bool check(int cnt) {
	for(int i=1;i<=n;i++) {
		father[i]=i;
	}
	for(int i=1;i<=cnt;i++) {
		tmp[i]=num[i];
	}
	sort(tmp+1,tmp+cnt+1,cmp);
	for(int i=1,j;i<=cnt;i=j+1) {
		j=i;
		int l=tmp[i].l;
		int r=tmp[i].r;
		int L=tmp[i].l;
		int R=tmp[i].r;
		while(j<cnt&&tmp[j].num==tmp[j+1].num) {
			j++;
			//求交集
			l=max(l,tmp[j].l);
			r=min(r,tmp[j].r);
			//求并集
			L=min(L,tmp[j].l);
			R=max(R,tmp[j].r);
		}
		//l,r记录的是交集的两个区间端点如果出现了l大于r那么出现矛盾了
		//第二个l>find(r)是根据区间染色确定的
		if(l>r||l>find(r)) return 0;
		//L、R记录的是并集的左右两个端点
		while(L<=R) {
		//如果并集区间内的点没有被染色就把他们合并到L-1这个点上 其实就可以看成一种区间染色
			if(find(R)==R){
				father[R]=find(L-1);
				R--;
			} 
			否则就去找它的父亲节点就是上面那个if里把它并到的那个点.... 仔细理解下就可以了 
			else R=father[R];
		}
	}
	//我们再来看这个判断if(l>find(r))它的含义也就是如果交集这个区间在之前是否出现过
	//因为我们是从大到小排序的如果当前的交集在之前出现过就证明这个交集中的最小值和当前的有矛盾
	return 1;
}
int Xiaobo()
{
	cin>>n>>q;
	for(int i=1;i<=q;i++) {
		cin>>num[i].l>>num[i].r>>num[i].num;
	}
	int l=1,r=q;
	int ans=0;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(check(mid)) {
			l=mid+1;	
		}
		else {
			ans=mid;
			r=mid-1;
		}
	}
	cout<<ans;
}

F、Telephone Lines

POJ3662
题目
链接:https://ac.nowcoder.com/acm/contest/1069/F
来源:牛客网

题目描述
Farmer John wants to set up a telephone line at his farm. Unfortunately, the phone company is uncooperative, so he needs to pay for some of the cables required to connect his farm to the phone system.
There are N (1 ≤ N ≤ 1,000) forlorn telephone poles conveniently numbered 1…N that are scattered around Farmer John’s property; no cables connect any them. A total of P (1 ≤ P ≤ 10,000) pairs of poles can be connected by a cable; the rest are too far apart.
The i-th cable can connect the two distinct poles Ai and Bi, with length Li (1 ≤ Li ≤ 1,000,000) units if used. The input data set never names any {Ai, Bi} pair more than once. Pole 1 is already connected to the phone system, and pole N is at the farm. Poles 1 and N need to be connected by a path of cables; the rest of the poles might be used or might not be used.
As it turns out, the phone company is willing to provide Farmer John with K (0 ≤ K < N) lengths of cable for free. Beyond that he will have to pay a price equal to the length of the longest remaining cable he requires (each pair of poles is connected with a separate cable), or 0 if he does not need any additional cables.
Determine the minimum amount that Farmer John must pay.
输入描述:

  • Line 1: Three space-separated integers: N, P, and K
  • Lines 2…P+1: Line i+1 contains the three space-separated integers: Ai, Bi, and Li
    输出描述:
  • Line 1: A single integer, the minimum amount Farmer John can pay. If it is impossible to connect the farm to the phone company, print -1.

题目大意:
就是给你一个图,从1-N中有许多边,我们把从1能到N的边称为一条路,找出每一条路中的第k+1大的元素,按照题目的意思就是,去掉每条道路的前k大个边,然后从这些第K+1大的边中选择最小的
刚开始我和队友用dfs暴力跑了一遍所有的路,然后找出每条能到n的路的第k+1大再比较找到最小的,当然这就是最直接的思路了,哇哇哇,菜的一批,果然TLE了,后来想了想 发现这个思路特别奇特 既然我们要输出的边的权值是在1-1e9范围之内的,哈哈 当然不会超过最大数据范围了,其实优化一下就是从1到我们输入的边中权值最大的,二分这个答案,然后验证答案是否符合,具体怎么验证呢 很关键的的是我们想要根据这个判断从而更改下次二分的区间,我们再来看一下题意,便会很清晰的发现,假如这个值就是答案,那么正好存在k个比它大的边,因为我们要找的最小的,所以这可以用最短路去保证,然后就是如果有比k大的边呢,说明答案不符合题意,答案太大了就要缩小区间右端点继续二分 ,那如果有比k小的边呢,说明这个答案太小了,扩大一下就有可能有k个比它小的边了,所以扩大左端点。最后就是怎么确定比它大的边数的个数呢? 我们就可以巧妙的把大于它的边标记为1小于等于它的边标记为0,跑一下最短路和K比较大小即可 熟悉的二分答案,写条件判断,上次比赛就没写出来,这次终于实现了。哈哈哈 直接看代码吧

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int INF=0x3f3f3f;
const int mod=1e9+7;
int n,p,k;
int num,adj[maxn],low[maxn],f[maxn],q[maxn];
struct edge
{
	int v,w,c,pre;
}e[maxn];
void insert(int u,int v,int w) {//邻接表存图
	e[num].v=v;
	e[num].c=w;
	e[num].pre=adj[u];
	adj[u]=num++;
}
int spfa(int x) {//最短路
	int i,v,head=0,tail=0;
	memset(low,0x7f,sizeof(low));
	low[x]=0;
	q[++tail]=x;
	while(head!=tail) {
		x=q[head=(head+1)%1005];
		f[x]=0;
		for(i=adj[x];~i;i=e[i].pre)
		{
			if(low[v=e[i].v]>low[x]+e[i].w) {
				low[v]=low[x]+e[i].w;
				if(!f[v]) {
					f[v]=1;
					q[tail=(tail+1)%1005]=v;
				}
			}
		}
	}
	return low[n]<=k;
}
int judge(int x) {//判断是否符合符合
	for(int i=1;i<=n;i++) {
		for(int j=adj[i];~j;j=e[j].pre) {//~j相当于j!=0
			if(e[j].c<=x) e[j].w=0;
			else e[j].w=1;
		}
	}
	return spfa(1);
}
int Xiaobo()
{
	cin>>n>>p>>k;
	num=0;
	memset(adj,-1,sizeof(adj));
	int l=0,r=0,mid,ans=-1;
	for(int i=0;i<p;i++) {
		int u,v,z;
		cin>>u>>v>>z;
		insert(u,v,z);
		insert(v,u,z);
		r=max(z,r);
	}
	while(l<=r) {
		int mid=(l+r)>>1;
		if(judge(mid)) {
			r=mid-1;
			ans=mid;
		}
		else {
			l=mid+1;
		}
	}
	cout<<ans<<endl;
}

Election Time

POJ3664
水题 有n头牛,第一列表示第一轮Ai的投票,第二列表示第二轮Bi的投票,在第一轮排名前k的可以进入第二轮,然后在第二轮找最大的票数,输出牛的序号即可
第一列从大到小排序,在第二列前k个里找个最大的。AC

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
struct st
{
	int l,r;
	int xb;
}num[maxn];
bool cmp(st a,st b) {
	if(a.l!=b.l) return a.l>b.l;
}
int Xiaobo()
{
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++) {
		cin>>num[i].l>>num[i].r;
		num[i].xb=i+1;
	}
	sort(num,num+n,cmp);
	int mx=num[0].r,ind=0;
	for(int i=1;i<k;i++) {
		if(num[i].r>mx) {
			mx=num[i].r;
			ind=i;
		}
	}
	cout<<num[ind].xb<<endl;
}

H、Cow Contest

POJ3660
题意
有N头牛,评以N个等级,各不相同,先给出部分牛的等级的高低关系,问最多能确定多少头牛的等级
解题思路:一头牛的等级,当且仅当它与其它N-1头牛的关系确定时确定,我就想着暴力dfs搜一遍就可以了,找出比当前牛等级大的个数,再找到比当前牛等级小的个数,二者相加如果等于n-1那么这个牛的等级就可以被确定 代码实现

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
int win[305][305];
int los[305][305];
int vis[305];
int n;
int cnt1=0;
void dfs1(int v) {
	for(int i=1;i<=n;i++) {
		if(win[v][i]&&!vis[i]) {
				vis[i]=1;
				cnt1++;
				dfs1(i);
		}
	}
}
void dfs2(int v) {
	for(int i=1;i<=n;i++) {
		if(los[v][i]&&!vis[i]) {
			vis[i]=1;
			cnt1++;
			dfs2(i);
		}
	}
}
int Xiaobo()
{
	int m;
	cin>>n>>m;
	while(m--) {
		int k,v;
		cin>>k>>v;
		win[k][v]=1;
		los[v][k]=1;
	}
	int cnt=0;
	for(int i=1;i<=n;i++) {
		memset(vis,0,sizeof(vis));
		cnt1=0;
		vis[i]=1;
		dfs1(i);
		memset(vis,0,sizeof(vis));
		vis[i]=1;
		dfs2(i);
		if(cnt1==n-1) {
			cnt++;
		}
	}
	cout<<cnt<<endl;
}

再挂一道水题

幂次方

洛谷1010

思路:听说正解是拿递归写的,可是我看了一眼数据范围之后就觉得它最大到2的15次方,打表直接暴力吧 哈哈

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<cmath>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
void inds(int x) {
    switch(x) {
        case 0:printf("2(0)");break;
        case 1:printf("2");break;
        case 2:printf("2(2)");break;
        case 3:printf("2(2+2(0))");break;
        case 4:printf("2(2(2))");break;
        case 5:printf("2(2(2)+2(0))");break;
        case 6:printf("2(2(2)+2)");break;
        case 7:printf("2(2(2)+2+2(0))");break;
        case 8:printf("2(2(2+2(0)))");break;
        case 9:printf("2(2(2+2(0))+2(0))");break;
        case 10:printf("2(2(2+2(0))+2)");break;
        case 11:printf("2(2(2+2(0))+2+2(0))");break;
        case 12:printf("2(2(2+2(0))+2+2)");break;
        case 13:printf("2(2(2+2(0))+2(2)+2(0))");break;
        case 14:printf("2(2(2+2(0))+2(2)+2)");break;
        case 15:printf("2(2(2+2(0))+2(2)+2+2(0))");break;
        case 16:printf("2(2(2+2)");break;
    }
}
int Xiaobo()
{
    int n;
    int num[20];
    for(int i=0;i<20;i++) {
        num[i]=(int)pow(2,i);
        //cout<<num[i]<<endl;
    }
    cin>>n;
    int cnt=0;
    while(n) {
        int ind=0;
        for(int i=0;i<20;i++) {
            if(n>=num[i]) ind=i;
            else break;
        }
        n-=num[ind];
        if(cnt) {
            printf("+");
        }
        inds(ind);
        cnt++;
    }
}

最后一道DP题

Running

POJ3661
题意:
题意:在赛道跑n分钟,每分钟可以选择跑或者休息,以分钟i跑时会跑完Di的距离但疲劳因子会+1,如果休息,每分钟疲劳因子会-1,但要减到0才能再次跑。疲劳因子不会超过m,在n分钟结束后,疲劳因子必须为0,求最大距离。注意结束的时候疲劳因子必须为0
思路
刚开始想着开两个dp ,分别记录当前分钟休息和跑的状态,后来POJ过了但是牛客空间超限,好烦不得不缩小空间,想了想用一个DP就可以实现,DP[i][j] i代表当前是第几分钟 j代表的就是疲劳度转态,0就是当前疲劳度为0的时候走的最远距离,同理到m… 然后就可以dp[i][0]用前一个状态及dp[i-1][0]确定 ,状态转移方程就是dp[i][j]=max(dp[i][j],dp[i-1][j-1]+num[i]);跟某个背包有点像哈哈 ,然后就是更新维护dp[i][0] dp[i][0]=max(dp[i][0],dp[i-k][k])为啥是dp[i-k][k]把k带进去的话就是dp[i-1][1]
dp[i-2][2]…就可以看到为什么了,它就是前一分钟的疲劳度为1的时候的值 前二分钟疲劳度为2的时候的值 … 中的最大值,假如说它休息的一分钟的话那么dp[i-1][1]状态就可以转移给dp[i][0] 同理 就很清楚了,代码也很容易实现了…0.0QAQ 全靠队友!哈哈 tql;
代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define Xiaobo main
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
int dp[10005][505];
int num[10005];
int Xiaobo()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) {
		cin>>num[i];
	}
	for(int i=1;i<=n;i++) {
		dp[i][0]=dp[i-1][0];
		for(int j=1;j<=m;j++) {
			dp[i][j]=max(dp[i][j],dp[i-1][j-1]+num[i]);
		}
		for(int k=1;k<=m;k++) {
			if(i-k>=0) {
				dp[i][0]=max(dp[i][0],dp[i-k][k]);
			}
		}
	}
	cout<<dp[n][0]<<endl;
}

最后就是这次我们队ak了然后所有的题也弄懂的差不多了,学到了很多知识,同时也看到了自己很多不足,很多题即使很经典,是模板题,但是如果把模板题理解透彻了那么也就成了自己的一部分思想了,最后还是要吹下那个二分答案的做法,感觉太巧妙了,DP的做法也理解了很多,呜呜呜,什么时候才能不那么弱呢,继续加油吧!!!哈哈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值