1. 程式人生 > >TYZ群賽 8/21解題報告

TYZ群賽 8/21解題報告


TYZ 8/21群賽 解題報告

賽制:OI

難度:約等於NOIP

題目來自網路::

下面有詳細的解說

T1

T1

竊賊和火柴

【問題描述】

一個竊賊進入了火柴倉庫,想要偷儘可能多的火柴。倉庫裡有 m 個集裝箱,

第 i 個集裝箱裡有 a

i 個火柴盒,每個火柴盒裡有 b

i 根火柴。所有火柴盒大小相同。

竊賊的帆布揹包恰能容納 n 個火柴盒。你的任務是找出竊賊能拿走的火柴的最大

數量。他沒時間重新調整火柴盒中的火柴,這就是他只是挑選不超過 n 個其包含

火柴數之和最大的火柴盒的原因。

【輸入檔案】

輸入檔案 bam.in 第一行包含整數 n(1≤n≤2·10

8

)和整數 m(1≤m≤20)。

第 i+1 行包含一對整數 a

i 和 b

i (1≤a

i≤10 8,1≤bi≤10)。所有輸入的數字都是整

數。

【輸出檔案】

輸出檔案 bam.out 包含唯一一個整數代表問題的答案。

【輸入樣例 1】

7 3

5 10

2 5

3 6

【輸出樣例 1】

62

【輸入樣例 2】

3 3

1 3

2 2

3 1

【輸出樣例 2】

7

這道題目的相似型應該是一次USACO的一道題:混合牛奶1

不過不用在意這些細節啦

這個題秒懂的貪心,先排序,選取容量最大的火柴盒子先進行拿,直到揹包裝滿為止.

程式碼實現方面,需要注意的是裝滿揹包時候的減法

不過還是10多分鐘就幹掉了;

程式碼如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<math.h>
using namespace std;
typedef long long ll;//這句是用來ll表示long long的數的
struct bx{
	ll a,b;
};
bx r[25];
ll n,m,k;
ll ans;
int cmp(bx x,bx y)
{
	return x.b>y.b;
}
int main()
{
	freopen("bam.in","r",stdin);
	freopen("bam.out","w",stdout);
	cin>>n>>m;
	for(ll i=1;i<=m;i++)
	{
		cin>>r[i].a>>r[i].b;
	}
	sort(r+1,r+1+m,cmp);
	ll nw=n;
	for(ll i=1;i<=m;i++)
	{
		if(nw>r[i].a)
		{
			nw-=r[i].a;
			ans+=r[i].a*r[i].b;
		}
		else
		{
			ans+=nw*r[i].b;
			break;
		}
	}
	cout<<ans<<endl;
	return 0;
}

第二題’:

名字叫“第三題”(。。。。。。。。。。。。。。)

原型是POJ的2161

Lamps-O-Matic 公司裝修很大的吊燈。吊燈由幾層組成。(從下至上)第一層

的水晶燈直接掛在環上。把環集起來以後再掛到上一層的環上面,依此類推。最

後一層是一個掛滿了燈和小環的大環(廢話)。

現在由機器人來做掛燈的事。機器人身上有資源補給,掛燈的時候,它使用

一個棧來儲存燈和環。一開始,這個棧是空的。機器人執行一個指令集來運作。

這個指令集是一個字串。

舉例說明:aaaaa3aaa2aaa45。a 代表將小燈放入棧中。數字 N 代表將目前棧

頂向下的 N 個資源取出,組成一個環,然後放回棧頂。整個棧的流程如下。

aaaaa → aa[aaa]  → aa[aaa]aaa → aa[aaa]a[aa]  → aa[aaa]a[aa]aaa →

aa[aaa]a[[aa]aaa]→END

這樣就需要棧空間的最大值為8,當其狀態為 aa[aaa]a[aa]aaa 時就需要,注

意:環和燈都算一個。

然而還有一個問題。機器人的棧不夠大了(真是次品)。所以需要你編一個

指令,使吊燈的設計不變,讓指令所需的棧空間越小越好。所謂的設計不變,就

是指:假設在同一層環原來有4 個部件,1234,那麼1234、4123、3412等等,只要環的順序不變就可以。

但是不能有部件增加或者減少。

【輸入檔案】

輸入檔案 three.in 每一行一個字串,代表指令。

【輸出檔案】

輸出檔案為three.out 包含一行,代表需要的棧空間

【輸入樣例】

aaaaa3aaa2aaa45

【輸出樣例】

6

【資料規模】

100%的資料字串長度≤10^4

想到了啥呢,剛開始的時候想到:

1.    可以貪心地放置,每次都先把成環的放進去,壓縮成環之後就可以省空間啊

2.    能先放大環就不要先放小環

3.    然而他是有順序的

4.    這道題目有些尷尬

那就繼續想了:

假設一個環子的掛的每個小環子都達到最優解,那麼這個環子就達到最優解。

換言之,只要計算出了每個環子的最優解,就可以計算出最優解。

第二,每個環子最多有9個子環

那就愉快的分治吧!

對於每個環,貪心和列舉差不了多少,貪心還不一定對,所以就列舉從環的哪個位置開始組裝吧

這個字串給得。。。。讓程式碼實現變得不是很簡單啊

我總不能用括號序列吧歪歪歪,那麼怎麼辦呢

可以不可以先找到數字,把串裡面的先字母后數字的順序調換一下。

有難度,為何不直接從後面……對,就從字串的菊花開始。

每次從後面找到數字之後就處理,用深度優先的遞迴來處理就好了。

經過兩個小時的修改,第二題就完工了。

#include<iostream>
#include<limits.h>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=10005;
struct pao{int fi,ls;
};
char s[maxn];
int n,m,k;
pao kong;
pao cal(int cur)
{
	pao res=kong;
	int nw=s[cur]-'0';
	int w=0;
	vector<int> po;
	for(int i=cur-1;;)
	{
		if(w==nw)
		{
			res.ls=i;
			break;
		}
		if(s[i]=='a')
		{
			w++;
			i--;
			po.push_back(1);
			continue;
		}
		if(s[i]>='0'&&s[i]<='9')
		{
			pao wi=cal(i);
			i=wi.ls;
			po.push_back(wi.fi);
			w++;
		}
	}
	int len=po.size();
	int tem=INT_MAX;
	/*
	cout<<"each line 's results:"<<endl;
	for(int i=0;i<len;i++)
	cout<<po[i]<<" ";
	cout<<endl;
	*/
	stack<int> hel;
	for(int i=0;i<len;i++)
	{
		hel.push(po[i]);
	}
	for(int i=0;i<len;i++)
	{
		int zc=hel.top();
		po[i]=zc;
		hel.pop();
	}
	/*
	cout<<"each line 's results:"<<endl;
	for(int i=0;i<len;i++)
	cout<<po[i]<<" ";
	cout<<endl;
	*/
	for(int i=0;i<len;i++)
	{
		int sum=0;
		int tm=0;
		bool shou=1;
		for(int j=INT_MAX;j!=i;)	
			{
				if(shou)
				{
					j=i;
					shou=0;
				}
				tm=max(tm,po[j]+sum);
				sum++;
				j=(j+1)%len;
			}
		tem=min(tem,tm);			
	}
	res.fi=tem;
	//cout<<"but the final results is :"<<tem<<endl;
	return res;
}
int sz=1;
int main()//請養成從主函式開始閱讀的好習慣!
{
	freopen("three.in","r",stdin);
	freopen("three.out","w",stdout);
	while(cin>>s[sz])
	sz++;
	pao ans=cal(sz-1);
	cout<<ans.fi<<endl;
	return 0;
}

註釋:

我是不會壓位的那種人,所以我就寫了結構體

所以說分治的返回值就比較挫

然後我是那種陣列運用不太熟練的人,於是我把字串反過來的時候就用了

出錯的地方:

1.    開始的時候sz這個輸入的游標就輸錯了應該從sz-1開始遞迴的,很尷尬

2.    然後開始的時候,在列舉環的位置的時候,雖然用了%,還是出錯,對於for迴圈的掌握不熟,所以導致gg

3.    我個強迫症,定義變數j之後不知道這玩意等不等於i,就設定成了0,然後完美wa了10分鐘。(設定成-1不就行了嘛,然後我就設成了INT_MAX)

4.    樣例開始的時候一直是5,後來發現是爆字串菊花然後vector的位置忘了翻過來,導致失誤。

把字串翻過之後就停手了,因為時間不多瞭然後我去敲第三題的暴力。

調整

【問題描述】

已給定一個 N 個點 M 條邊的有向圖,點編號為 1 到 N,第 i 條邊為(UI,vi)

權值為 wi。你可以進行一次操作,使得任意一條邊的權值變成任意非負整數。要

求進行儘量少的操作次數,使得點 1 到點 N 的最短路徑長度變成 c。

題目保證,c 小於在未進行任何操作之前的原圖中 1 到 N 的最短路長度。

【輸入檔案】

輸入檔案 tweak.in 第一行三個整數,N,M 和 c

接下來 M 行,每行一條邊的資訊(UI,vi)

和 wi,第 i 行的表述第 i 條邊的資訊。

保證不會有自環存在,對於不同的 i 和 j,

(ui,vi)不同於(uj,vj) 。

【輸出檔案】

輸出檔案 tweak.out 一行一個整數,要進行最少多少次操作才能使得最短路

長度變為 c。

【輸入樣例】

3 3 3

1 2 3

2 3 3

1 3 8

【輸出樣例】

1

【樣例說明】

將邊 1,3 的權值修改為 2 就可以了。

【資料規模】

N≤100

M≤1000

0≤c≤100000

0≤wi≤10000

30%資料滿足 M≤20

50%的資料滿足 M≤70

以為自己這次模擬賽又要墊底了,當時想死的心都有了,電光火石之間突然第二題瞬間就有把握了?!,整個人都沸騰了!

只剩十分鐘了,抄起傢伙,準備暴力,思路就是dfs出來所有路徑,然後計算需要的減去的次數,記錄路徑用模擬棧和回溯

如期交上暴力40/100

暴力程式碼如下,好像不太對,wa了一個,剩下的都是re


註釋:

我是不會壓位的那種人,所以我就寫了結構體

所以說分治的返回值就比較挫

然後我是那種陣列運用不太熟練的人,於是我把字串反過來的時候就用了

出錯的地方:

1.    開始的時候sz這個輸入的游標就輸錯了應該從sz-1開始遞迴的,很尷尬

2.    然後開始的時候,在列舉環的位置的時候,雖然用了%,還是出錯,對於for迴圈的掌握不熟,所以導致gg

3.    我個強迫症,定義變數j之後不知道這玩意等不等於i,就設定成了0,然後完美wa了10分鐘。(設定成-1不就行了嘛,然後我就設成了INT_MAX)

4.    樣例開始的時候一直是5,後來發現是爆字串菊花然後vector的位置忘了翻過來,導致失誤。

把字串翻過之後就停手了,因為時間不多瞭然後我去敲第三題的暴力。

調整

【問題描述】

已給定一個 N 個點 M 條邊的有向圖,點編號為 1 到 N,第 i 條邊為(UI,vi)

權值為 wi。你可以進行一次操作,使得任意一條邊的權值變成任意非負整數。要

求進行儘量少的操作次數,使得點 1 到點 N 的最短路徑長度變成 c。

題目保證,c 小於在未進行任何操作之前的原圖中 1 到 N 的最短路長度。

【輸入檔案】

輸入檔案 tweak.in 第一行三個整數,N,M 和 c

接下來 M 行,每行一條邊的資訊(UI,vi)

和 wi,第 i 行的表述第 i 條邊的資訊。

保證不會有自環存在,對於不同的 i 和 j,

(ui,vi)不同於(uj,vj) 。

【輸出檔案】

輸出檔案 tweak.out 一行一個整數,要進行最少多少次操作才能使得最短路

長度變為 c。

【輸入樣例】

3 3 3

1 2 3

2 3 3

1 3 8

【輸出樣例】

1

【樣例說明】

將邊 1,3 的權值修改為 2 就可以了。

【資料規模】

N≤100

M≤1000

0≤c≤100000

0≤wi≤10000

30%資料滿足 M≤20

50%的資料滿足 M≤70

以為自己這次模擬賽又要墊底了,當時想死的心都有了,電光火石之間突然第二題瞬間就有把握了?!,整個人都沸騰了!

只剩十分鐘了,抄起傢伙,準備暴力,思路就是dfs出來所有路徑,然後計算需要的減去的次數,記錄路徑用模擬棧和回溯

如期交上暴力40/100

暴力程式碼如下,好像不太對,wa了一個,剩下的都是re

註釋:

我是不會壓位的那種人,所以我就寫了結構體

所以說分治的返回值就比較挫

然後我是那種陣列運用不太熟練的人,於是我把字串反過來的時候就用了

出錯的地方:

1.開始的時候sz這個輸入的游標就輸錯了應該從sz-1開始遞迴的,很尷尬

2.然後開始的時候,在列舉環的位置的時候,雖然用了%,還是出錯,對於for迴圈的掌握不熟,所以導致gg

3.我個強迫症,定義變數j之後不知道這玩意等不等於i,就設定成了0,然後完美wa10分鐘。(設定成-1不就行了嘛,然後我就設成了INT_MAX

4.樣例開始的時候一直是5,後來發現是爆字串菊花然後vector的位置忘了翻過來,導致失誤。

把字串翻過之後就停手了,因為時間不多瞭然後我去敲第三題的暴力。

調整

【問題描述】

已給定一個 N個點 M 條邊的有向圖,點編號為 1 N,第 i條邊為(UIvi

權值為 wi。你可以進行一次操作,使得任意一條邊的權值變成任意非負整數。要

求進行儘量少的操作次數,使得點 1到點 N 的最短路徑長度變成 c

題目保證,c小於在未進行任何操作之前的原圖中 1 N 的最短路長度。

【輸入檔案】

輸入檔案 tweak.in第一行三個整數,NM c

接下來 M行,每行一條邊的資訊(UIvi

wi,第 i行的表述第 i 條邊的資訊。

保證不會有自環存在,對於不同的 i j

uivi)不同於(ujvj) 。

【輸出檔案】

輸出檔案 tweak.out一行一個整數,要進行最少多少次操作才能使得最短路

長度變為 c

【輸入樣例】

3 3 3

1 2 3

2 3 3

1 3 8

【輸出樣例】

1

【樣例說明】

將邊 13的權值修改為 2 就可以了。

【資料規模】

N100

M1000

0c100000

0wi10000

30%資料滿足 M20

50%的資料滿足 M70

以為自己這次模擬賽又要墊底了,當時想死的心都有了,電光火石之間突然第二題瞬間就有把握了?!,整個人都沸騰了!

只剩十分鐘了,抄起傢伙,準備暴力,思路就是dfs出來所有路徑,然後計算需要的減去的次數,記錄路徑用模擬棧和回溯

如期交上暴力40/100

暴力程式碼如下,好像不太對,wa了一個,剩下的都是re

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
struct ed{
	int u,v,w;
};
ed a[1005];
int e[105][105];
int t1,t2,t3;
int st[1105];
int tp;
int n,m,c;
int ANS=INT_MAX;
void dfs(int x)
{
	if(x==n)
	{
		priority_queue<int> p;
		int sum=0;
		for(int i=0;i<tp;i++)
		{
			p.push(st[i]);
			sum+=st[i];
		}
		int ned=sum-c;
		int ans=0;
		while(ned>0)
		{
			int nw=p.top();
			p.pop();
			if(nw<ned)
			{
				ned-=nw;
				ans++;
			}
			else
			{
				ned=0;
				ans++;
				ANS=min(ANS,ans);
				return;
			}
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		if(e[x][i])
		{
			st[tp]=e[x][i];
			tp++;
			dfs(i);
			tp--;
		}
	}
}
int main()
{
	freopen("tweak.in","r",stdin);
	freopen("tweak.out","w",stdout);
	cin>>n>>m>>c;
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&t1,&t2,&t3);
		e[t1][t2]=t3;
	}
	dfs(1);
	cout<<ANS<<endl;
	return 0;
}


正解在下面

#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
using namespace std; 
int n,m,c,cur[111],num,t1,t2,t3;
int l,r,d[104][100111],ans;
struct edge{int to,v,frh;}a[50001];
struct queue{int z,y;}q[5000001];

int main(){
   
    scanf("%d%d%d",&n,&m,&c);
    for (int i=1; i<=n; i++)
      {
        cur[i]=i;
      }     
    num=n;
    for (int i=1; i<=m; i++)
      {
        scanf("%d%d%d",&t1,&t2,&t3);
        num++; a[num].to=t2; a[num].v=t3; a[cur[t1]].frh=num; cur[t1]=num;
      }
      
	   
    memset(d,-1,sizeof d);
    l=r=1; q[1].z=1; q[l].y=0;d[1][0]=0;//
    while (l<=r)
      {
          int t=q[l].z;
          while (a[t].frh!=0)
            {
                t=a[t].frh;
                //尋找來邊
				 int nz=q[l].z;
				 int ny=q[l].y;
				 int nv=a[t].v;
				 int nd=d[nz][ny];
				 int nx=d[a[t].to][q[l].y+a[t].v];
                if (nz+nv<=c && 
                    (nd<nx||nx==-1))
                  {
                    nx=nd;
                    r++; q[r].z=a[t].to; q[r].y=q[l].y+a[t].v; 
                    
                  }
                  //由於上一步修改了部分,所有的變數重新定義(反正複製貼上) 
                   nz=q[l].z;
				  ny=q[l].y;
				  nv=a[t].v;
				  nd=d[nz][ny];
				  nx=d[a[t].to][q[l].y+a[t].v];
				 int ns=d[q[l].z][q[l].y];
				int nm=d[a[t].to][q[l].y];
                if (ns+1<nm||nm==-1)
                
                  {
                    d[a[t].to][q[l].y]=nm=ns+1;
                    r++; q[r].z=a[t].to; q[r].y=q[l].y;
                  }
            }
          l++;
      }
    ans=INT_MAX;
    for (int i=0; i<=c; i++)
        if (d[n][i]<ans && d[n][i]!=-1)
           ans=d[n][i];
    cout<<ans<<endl; 
    return 0;
}

總分:

1 100/100

2 100/100

3 40/100