ICPC 2017 Japan Tsukuba(solve5/11)

題目鏈接:可以直接去Vj開題目 題目編號Aizu 1378-1388

A題

題意:給你一個容器,只能填方黑色和白色巧克力,黑色有1和k 2種規格,白色爲1,問你在不超過l的情況下而且保證地段時黑色的情況下的方案數。

思想:DP  DP[i][j]  i=0爲黑 1爲白 i=0時候dp[i][j]=dp[1][j-1] i=1的時候 dp[i][j]=dp[i-1][j-1] + dp[i-1][j-k]//當j>=k的時候

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[2][105];//0黑1白 
int main()
{
	int l,k;
	while(scanf("%d%d",&l,&k)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		dp[0][1]=1ll;
		dp[0][k]=1ll;
		for(int i=2;i<=l;i++)
		{
			dp[0][i]+=dp[1][i-1];
			dp[1][i]+=dp[0][i-1];
			if(i>=k)
				dp[1][i]+=dp[0][i-k];
		}
		ll ans=0;
		for(int i=1;i<=l;i++)
			ans+=dp[0][i];
		printf("%lld\n",ans);
	}
	return 0;
} 

B題

題意:給你n個點,問你最多可以有多少平行線,2個點只能連接一條線,一個點只能在一個線內。

思想:本來直接貪心斜率相等然後在算上垂直的,然後WA。想了想可能會出現很多斜率不等的,但是都可以湊成一對一對的,這樣就只能爆搜了,直接dfs搜索。剪枝即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct node{
	int x;
	int y;
}no[20];
int ans,n;
int k1[20];
int k2[20];
int vis[20];
void dfs(int id,int sum)
{
	if(sum>=n/2)
	{
		int Ans=0;
		for(int i=0;i<sum;i++)
			for(int j=i+1;j<sum;j++)
				if(k1[i]*k2[j]==k2[i]*k1[j])
					Ans++;
		ans=max(ans,Ans);	
		return ;
	}
	if(vis[id])//當前被搞了 
		dfs(id+1,sum);
	else
	{
		for(int i=0;i<n;i++)
		{
			if(vis[i] || id==i)
				continue;
			k1[sum]=(no[i].y-no[id].y);
			k2[sum]=(no[i].x-no[id].x);
			vis[i]=1;
			vis[id]=1;
			dfs(id+1,sum+1);
			vis[i]=0;
			vis[id]=0;
		}
	} 
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	scanf("%d%d",&no[i].x,&no[i].y);
	dfs(0,0);
	printf("%d\n",ans);
}

 

C題

題意:給你n個人和一個t,每個人堅持每項的時間固定的a[i],問你t的時候n個人在檢查或者等待檢查某個項目。

思想:考慮下對於某個人來說他的最壞時間肯定是前邊那個花費最多時間的那個人檢查的項目,可以畫畫那個邏輯關係。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll  a[maxn];
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);
		ll ans=0;
		ll temp = -1;
		for(int i=0;i<n;i++)
		{
			ans+=a[i];
			temp=max(temp,a[i]);
			if(m>=ans)
				printf("%lld\n",(m-ans)/temp+2);
			else
				printf("1\n");
		}
	}
	return 0;
}

F題

題意:給定一個有重邊的帶正權有向圖,保證一開始有從 1 到 2 的路徑。對於每條邊,詢問把它反向之後,從 1 到 2 的最短路如何變化。

思想:考慮翻轉某條邊對結果是否有影響,如果當前邊是DAG圖上的橋的話,肯定會讓結果變差,如果不是橋的話,肯定是不變或者變小。重點是判斷當前邊是否是DAG圖上的橋,就需要tarjan縮點判斷是否是橋了。

當結點u的子結點v的後代通過反向邊只能連回v,那麼刪除這條邊(u, v)就可以使得圖G非連通了。用Tarjan算法裏面的時間戳表示這個條件,就是low[v]>dfn[u]。 

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct node{
	int to;
	int valu;
	int next;
	int flag;
}no[maxn],No[maxn*2];//正向 反向 
int head[maxn],Head[maxn];
int cnt,Cnt;
ll dist[maxn];//正向 
ll Dist[maxn];//反向 
void add(int u,int v,int valu,int flag)
{
	no[cnt].to=v;
	no[cnt].valu=valu;
	no[cnt].flag=flag;
	no[cnt].next=head[u];
	head[u]=cnt++;
} 
void Add(int u,int v,int valu,int flag)
{
	No[Cnt].to=v;
	No[Cnt].valu=valu;
	No[Cnt].flag=flag;
	No[Cnt].next=Head[u];
	Head[u]=Cnt++;
} 
void spfa(int u)
{
	memset(dist,INF,sizeof(dist));
	int vis[maxn]={0};
	queue<int>q;
	q.push(u);
	vis[u]=1;
	dist[u]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i!=-1;i=no[i].next)
		{
			int v=no[i].to;
			if(dist[v]>dist[u]+no[i].valu)
			{
				dist[v]=dist[u]+no[i].valu;
				if(vis[v]==0)
				{
					vis[v]=1;
					q.push(v); 
				}
			}
		}
	}
}
void Spfa(int u)
{
	memset(Dist,INF,sizeof(Dist));
	int vis[maxn]={0};
	queue<int>q;
	q.push(u);
	vis[u]=1;
	Dist[u]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=Head[u];i!=-1;i=No[i].next)
		{
			int v=No[i].to;
			if(Dist[v]>Dist[u]+No[i].valu)
			{
				Dist[v]=Dist[u]+No[i].valu;
				if(vis[v]==0)
				{
					vis[v]=1;
					q.push(v); 
				}
			}
		}
	}
}
int dfn[maxn];
int low[maxn];
int is_bride[maxn];
int clo=1; 
void tarjan(int u,int pre)
{
	dfn[u]=low[u]=clo++;
	for(int i=Head[u];i!=-1;i=No[i].next)
	{
		int v=No[i].to;
		if(v!=pre)
		{
			if(dfn[v]==0)
			{
				tarjan(v,u);
				low[u]=min(low[u],low[v]);
				if(low[v] > dfn[u])
					is_bride[No[i].flag]=1;
			}
			else
				low[u]=min(low[u],low[v]);
		}
	}
}
int main()
{
	cnt=0;
	Cnt=0;
	memset(head,-1,sizeof(head));
	memset(Head,-1,sizeof(Head));
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,valu;
		scanf("%d%d%d",&u,&v,&valu);
		add(u,v,valu,i);
		Add(v,u,valu,i);
	} 
	spfa(1);
	Spfa(2);	
	memset(Head,-1,sizeof(Head));
	Cnt=0;
	ll Max=dist[2];
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j!=-1;j=no[j].next)
		{
			int v=no[j].to;
			if(dist[i]+Dist[v]+no[j].valu==Max)
			{
				
				Add(i,v,1,no[j].flag);
				Add(v,i,1,no[j].flag);
			}
		}
	}
	tarjan(1,-1);
	int ans[maxn]={0};
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j!=-1;j=no[j].next)
		{
			if(dist[no[j].to]+Dist[i]+no[j].valu<Max)
				ans[no[j].flag]=1;
			else if(is_bride[no[j].flag])
				ans[no[j].flag]=-1;
		}
	}
	for(int i=1;i<=m;i++)
	{
		if(ans[i]==1)
			printf("HAPPY\n");
        else if(ans[i]==-1)
			printf("SAD\n");
        else
            printf("SOSO\n");
	}
	return 0;
 } 

I題

題意:有n個人乘車,有兩種不同的預定票的方式,問你他們分別需要預留出來的最少的座位數量。但是保證所有人都坐下

①方法是允許客戶隨便 預定,就是想要那個就給你那個。②是最優的那種分配。

思想:對於第二種簡單,需要最多的肯定是同時上車人數最多的時候。第一種的話,肯定是對於這n個人中某個人預定的區間的人數的最大值。

兩個vis大小不一 。。

#include<bits/stdc++.h>
using namespace std;
int a[200005];
int b[200005];
int vis[100005];//標記上車 
int Vis[100005];//標記下車
int R[100005];//標記站點人 
int main()
{
    int n;
    scanf("%d",&n);
    int Max=-1;
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        vis[a[i]]++;//a[i]上車的人數 
        Vis[b[i]]++; //b[i]下車的人數
        R[a[i]]++;//記錄總人數 
        R[b[i]]--; 
        Max=max(Max,b[i]);
    }
    long long ans1=0;
    long long ans2=0;
    long long temp=0;
    for(int i=1;i<=Max;i++)
    {
        temp+=R[i];
        ans2=max(temp,ans2);
        vis[i]+=vis[i-1];
        Vis[i]+=Vis[i-1];
    }
    for(int i=0;i<=n;i++)
        ans1=max(ans1,(long long)vis[b[i]-1]-Vis[a[i]]);//考慮的是區間最大值
         
    printf("%lld %lld\n",ans1,ans2);    
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章